Skip to content

Commit 7d434a3

Browse files
committed
feat: add bar axis valueFormatter and fix halfChart path
1 parent a6eacf6 commit 7d434a3

File tree

14 files changed

+295
-24
lines changed

14 files changed

+295
-24
lines changed

src/charts/barChart/barChart.type.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { TickData } from '@/components/tick/tick.types';
88
import type { CanvasConfig } from '@/types/canvas.type';
99
import type { ChartError, ChartErrorCollection, ErrorType } from '@/types/errors.type';
1010
import type { Positions } from '@/types/position.enum';
11+
import type { ValueFormatter } from '@/types/valueFormatter.type';
1112

1213
export type BarChartChildrenType = ReactNode | ReactElement<PathProps | XAxisProps | YAxisProps>;
1314

@@ -121,13 +122,15 @@ export interface BarChartXAxisProps extends Omit<XAxisProps, OmitProps>, React.A
121122
*/
122123
ariaLabel?: string;
123124
tickValues?: BarChartTickValuesAxisProps;
125+
valueFormatter?: ValueFormatter<string>;
124126
}
125127
export interface BarChartYAxisProps extends Omit<YAxisProps, OmitProps>, React.AriaAttributes {
126128
/**
127129
* @deprecated Use aria-label instead for better accessibility standards
128130
*/
129131
ariaLabel?: string;
130132
tickValues?: BarChartTickValuesAxisProps;
133+
valueFormatter?: ValueFormatter<string>;
131134
}
132135
export interface BarChartSeparatorProps {
133136
topSeparator?: StyleProps;

src/charts/barChart/fragments/__tests__/barChartXAxis.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,20 @@ describe('LineChartXAxis', () => {
1515
const xAxis = getByTestId('testxAxis');
1616
expect(xAxis).toBeInTheDocument();
1717
});
18+
19+
it('renders formatted tick labels', () => {
20+
const { getByText } = render(
21+
<BarChartContext.Provider value={CONTEXT}>
22+
<BarChart.XAxis
23+
tickLine={{}}
24+
tickText={{ fontSize: 12 }}
25+
valueFormatter={value => `Label ${value}`}
26+
/>
27+
</BarChartContext.Provider>
28+
);
29+
30+
expect(getByText('Label 1')).toBeInTheDocument();
31+
expect(getByText('Label 2')).toBeInTheDocument();
32+
expect(getByText('Label 3')).toBeInTheDocument();
33+
});
1834
});

src/charts/barChart/fragments/__tests__/barChartYAxis.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,20 @@ describe('LineChartYAxis', () => {
1515
const yAxis = getByTestId('testyAxis');
1616
expect(yAxis).toBeInTheDocument();
1717
});
18+
19+
it('renders formatted tick labels', () => {
20+
const { getByText } = render(
21+
<BarChartContext.Provider value={CONTEXT}>
22+
<BarChart.YAxis
23+
tickLine={{}}
24+
tickText={{ fontSize: 12 }}
25+
valueFormatter={value => `${value}%`}
26+
/>
27+
</BarChartContext.Provider>
28+
);
29+
30+
expect(getByText('10%')).toBeInTheDocument();
31+
expect(getByText('20%')).toBeInTheDocument();
32+
expect(getByText('30%')).toBeInTheDocument();
33+
});
1834
});

src/charts/barChart/fragments/barChartXAxis.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type FC, type ReactElement, useContext } from 'react';
22

33
import { XAxis } from '@/components/axisChart/xAxis/xAxis';
4+
import { TickDataUtils } from '@/components/tick/tick.types';
45
import { Positions } from '@/types/position.enum';
56
import { getTickTextYCoordinate } from '@/utils/getTickTextCoordinate/getTickTextCoordinates';
67

@@ -12,6 +13,7 @@ export const BarChartXAxis: FC<BarChartXAxisProps> = ({
1213
position = Positions.BOTTOM,
1314
tickLine,
1415
tickText,
16+
valueFormatter = (value: string) => value,
1517
...props
1618
}): ReactElement => {
1719
const {
@@ -29,6 +31,9 @@ export const BarChartXAxis: FC<BarChartXAxisProps> = ({
2931

3032
const y1 = context.extraSpaceTopY;
3133
const y2 = Number(context.canvasHeight) - context.extraSpaceBottomY;
34+
const formattedTickValues = tickText
35+
? TickDataUtils.formatTicksValues(tickValues, valueFormatter)
36+
: undefined;
3237

3338
return (
3439
<XAxis
@@ -51,7 +56,7 @@ export const BarChartXAxis: FC<BarChartXAxisProps> = ({
5156
...tickText,
5257
y: tickTextY,
5358
}}
54-
tickValues={tickText ? tickValues : undefined}
59+
tickValues={formattedTickValues}
5560
/>
5661
);
5762
};

src/charts/barChart/fragments/barChartYAxis.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { type FC, type ReactElement, useContext } from 'react';
22

33
import { YAxis } from '@/components/axisChart/yAxis/yAxis';
4+
import { TickDataUtils } from '@/components/tick/tick.types';
45
import { Positions } from '@/types/position.enum';
56
import { ajustedTextSpace } from '@/utils/ajustedTextSpace/ajustedTextSpace';
67
import { getTickTextXCoordinate } from '@/utils/getTickTextCoordinate/getTickTextCoordinates';
@@ -13,6 +14,7 @@ export const BarChartYAxis: FC<BarChartYAxisProps> = ({
1314
position = Positions.LEFT,
1415
tickLine,
1516
tickText,
17+
valueFormatter = (value: string) => value,
1618
...props
1719
}): ReactElement => {
1820
const {
@@ -30,6 +32,9 @@ export const BarChartYAxis: FC<BarChartYAxisProps> = ({
3032
coordinates.x1,
3133
ajustedText
3234
);
35+
const formattedTickValues = tickText
36+
? TickDataUtils.formatTicksValues(tickValues, valueFormatter)
37+
: undefined;
3338

3439
return (
3540
<YAxis
@@ -44,7 +49,7 @@ export const BarChartYAxis: FC<BarChartYAxisProps> = ({
4449
x2: Number(context.canvasWidth) - context.extraSpaceRightX,
4550
}}
4651
tickText={{ ...tickText, x: xTickText }}
47-
tickValues={tickText ? tickValues : undefined}
52+
tickValues={formattedTickValues}
4853
/>
4954
);
5055
};

src/charts/barChart/stories/children/XAxis/xAxis.argtypes.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,26 @@ export const xAxisArgTypes = (): ArgTypes<BarChartXAxisProps> => {
181181
},
182182
},
183183

184+
valueFormatter: {
185+
control: {
186+
labels: {
187+
currency: 'Currency ($)',
188+
custom: 'Custom Format [val]',
189+
millions: 'Millions (M)',
190+
none: 'None (no formatting)',
191+
percentage: 'Percentage (%)',
192+
thousands: 'Thousands (K)',
193+
},
194+
type: 'select',
195+
},
196+
description: 'Select a formatting style for tick labels in this story.',
197+
options: ['none', 'currency', 'percentage', 'thousands', 'millions', 'custom'],
198+
table: {
199+
category: CATEGORY_CONTROL.DATA,
200+
type: { summary: 'ValueFormatter | undefined' },
201+
},
202+
},
203+
184204
transform: {
185205
control: { type: 'text' },
186206
description: 'SVG transform attribute for positioning and scaling.',

src/charts/barChart/stories/children/XAxis/xAxis.stories.tsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@ import { BarOrientation } from '@/components/bar/bar.type';
44
import { Note } from '@/storybook/components/note/note';
55
import { DefaultCanvasConfig } from '@/types/canvas.type';
66
import { Positions } from '@/types/position.enum';
7+
import type { ValueFormatter } from '@/types/valueFormatter.type';
78

89
import { BarChart } from '../../../barChart';
910
import type { BarChartXAxisProps } from '../../../barChart.type';
10-
import { BarChartXAxis } from '../../../fragments/barChartXAxis';
1111
import { xAxisArgTypes } from './xAxis.argtypes';
1212

13+
type XAxisStoryArgs = Omit<BarChartXAxisProps, 'valueFormatter'> & {
14+
valueFormatter?: ValueFormatter | string;
15+
};
16+
1317
const meta = {
1418
argTypes: xAxisArgTypes(),
15-
component: BarChartXAxis,
1619
decorators: [
1720
(Story: React.ComponentType) => (
1821
<>
@@ -33,6 +36,11 @@ const meta = {
3336
<strong>tickValues</strong> - Define custom tick positions using numeric or custom
3437
format
3538
</>,
39+
<>
40+
<strong>valueFormatter</strong> - Format tick labels using a callback function{' '}
41+
<code>(val) =&gt; string</code>. The dropdown below shows preset examples, but in your
42+
code you'll pass actual functions.
43+
</>,
3644
<>
3745
<strong>tickText</strong> - Complete text styling control including font, color, and
3846
positioning
@@ -61,10 +69,28 @@ const meta = {
6169
],
6270
tags: ['autodocs'],
6371
title: 'Charts/BarChart/Child Components/BarChartXAxis',
64-
} satisfies Meta<typeof BarChartXAxis>;
72+
} satisfies Meta<XAxisStoryArgs>;
6573

6674
export default meta;
67-
type Story = StoryObj<typeof meta>;
75+
type Story = StoryObj<XAxisStoryArgs>;
76+
77+
const getValueFormatter = (formatterType: string) => {
78+
switch (formatterType) {
79+
case 'currency':
80+
return val => `$${val}`;
81+
case 'percentage':
82+
return val => `${val}%`;
83+
case 'thousands':
84+
return val => `${val}K`;
85+
case 'millions':
86+
return val => `${val}M`;
87+
case 'custom':
88+
return val => `[${val}]`;
89+
case 'none':
90+
default:
91+
return undefined;
92+
}
93+
};
6894

6995
export const XAxisCustomization: Story = {
7096
args: {
@@ -159,11 +185,17 @@ export const XAxisCustomization: Story = {
159185
values: ['2001', '2002', '2003', '2004'],
160186
},
161187
},
188+
valueFormatter: 'thousands',
162189

163190
transform: undefined,
164191
},
165192

166-
render: (args: BarChartXAxisProps) => {
193+
render: (args: XAxisStoryArgs) => {
194+
const actualArgs = {
195+
...args,
196+
valueFormatter: getValueFormatter(args.valueFormatter as string),
197+
};
198+
167199
// Simplified data for better visualization (single series per year)
168200
const simplifiedData = [
169201
{ value: 50, year: 2001 },
@@ -208,7 +240,7 @@ export const XAxisCustomization: Story = {
208240
orientation={BarOrientation.VERTICAL}
209241
pKey="year"
210242
>
211-
<BarChart.XAxis {...args} />
243+
<BarChart.XAxis {...actualArgs} />
212244
{simplifiedData.map((dataPoint, index) => (
213245
<BarChart.Path
214246
key={`bar-${dataPoint.year}`}

src/charts/barChart/stories/children/YAxis/yAxis.argtypes.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,28 @@ export const yAxisArgTypes = (): ArgTypes<BarChartYAxisProps> => {
172172
},
173173
},
174174

175+
valueFormatter: {
176+
control: {
177+
labels: {
178+
currency: 'Currency ($)',
179+
custom: 'Custom brackets [val]',
180+
millions: 'Millions (M)',
181+
none: 'None (no formatting)',
182+
percentage: 'Percentage (%)',
183+
thousands: 'Thousands (K)',
184+
units: 'Units (k)',
185+
},
186+
type: 'select',
187+
},
188+
description: 'Select a formatting style for tick labels in this story.',
189+
options: ['none', 'currency', 'percentage', 'units', 'thousands', 'millions', 'custom'],
190+
table: {
191+
category: CATEGORY_CONTROL.DATA,
192+
defaultValue: { summary: 'undefined' },
193+
type: { summary: 'ValueFormatter' },
194+
},
195+
},
196+
175197
transform: {
176198
control: { type: 'text' },
177199
description: 'SVG transform attribute for positioning and scaling.',

src/charts/barChart/stories/children/YAxis/yAxis.stories.tsx

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ import { BarOrientation } from '@/components/bar/bar.type';
44
import { Note } from '@/storybook/components/note/note';
55
import { DefaultCanvasConfig } from '@/types/canvas.type';
66
import { Positions } from '@/types/position.enum';
7+
import type { ValueFormatter } from '@/types/valueFormatter.type';
78

89
import { BarChart } from '../../../barChart';
910
import type { BarChartYAxisProps } from '../../../barChart.type';
10-
import { BarChartYAxis } from '../../../fragments/barChartYAxis';
1111
import { simplifiedData } from '../../templates/data';
1212
import { yAxisArgTypes } from './yAxis.argtypes';
1313

14+
type YAxisStoryArgs = Omit<BarChartYAxisProps, 'valueFormatter'> & {
15+
valueFormatter?: ValueFormatter | string;
16+
};
17+
1418
const meta = {
1519
argTypes: yAxisArgTypes(),
16-
component: BarChartYAxis,
1720
decorators: [
1821
(Story: React.ComponentType) => (
1922
<>
@@ -30,6 +33,11 @@ const meta = {
3033
<>
3134
<strong>Key Features:</strong>
3235
</>,
36+
<>
37+
<strong>valueFormatter</strong> - Format tick labels using a callback function{' '}
38+
<code>(val) =&gt; string</code>. The dropdown below shows preset examples, but in your
39+
code you'll pass actual functions.
40+
</>,
3341
<>
3442
<strong>position</strong> - Axis placement (left or right, refresh story after
3543
setting in controls)
@@ -68,10 +76,30 @@ const meta = {
6876
],
6977
tags: ['autodocs'],
7078
title: 'Charts/BarChart/Child Components/BarChartYAxis',
71-
} satisfies Meta<typeof BarChartYAxis>;
79+
} satisfies Meta<YAxisStoryArgs>;
7280

7381
export default meta;
74-
type Story = StoryObj<typeof meta>;
82+
type Story = StoryObj<YAxisStoryArgs>;
83+
84+
const getValueFormatter = (formatterType: string) => {
85+
switch (formatterType) {
86+
case 'currency':
87+
return val => `$${val}`;
88+
case 'percentage':
89+
return val => `${val}%`;
90+
case 'thousands':
91+
return val => `${val}K`;
92+
case 'millions':
93+
return val => `${val}M`;
94+
case 'units':
95+
return val => `${val}k`;
96+
case 'custom':
97+
return val => `[${val}]`;
98+
case 'none':
99+
default:
100+
return undefined;
101+
}
102+
};
75103

76104
export const YAxisCustomization: Story = {
77105
args: {
@@ -161,11 +189,17 @@ export const YAxisCustomization: Story = {
161189
step: 1,
162190
},
163191
},
192+
valueFormatter: 'currency',
164193

165194
transform: undefined,
166195
},
167196

168-
render: (args: BarChartYAxisProps) => {
197+
render: (args: YAxisStoryArgs) => {
198+
const actualArgs = {
199+
...args,
200+
valueFormatter: getValueFormatter(args.valueFormatter as string),
201+
};
202+
169203
const chartData = simplifiedData;
170204

171205
const barConfigs = [
@@ -203,7 +237,7 @@ export const YAxisCustomization: Story = {
203237
orientation={BarOrientation.HORIZONTAL}
204238
pKey="year"
205239
>
206-
<BarChart.YAxis {...args} />
240+
<BarChart.YAxis {...actualArgs} />
207241

208242
{chartData.map((dataPoint, index) => (
209243
<BarChart.Path

0 commit comments

Comments
 (0)