Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions change/@fluentui-react-charts-function-tickformat.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: allow a function for the cartesian tickFormat so the numeric value axis can render a literal % or route through an app/i18n formatter (string behavior unchanged)",
"packageName": "@fluentui/react-charts",
"email": "michael@xerilium.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ export interface CartesianChartProps {
strokeWidth?: number;
styles?: CartesianChartStyles;
svgProps?: React_2.SVGProps<SVGSVGElement>;
tickFormat?: string;
tickFormat?: string | ((value: number | Date) => string);
tickPadding?: number;
tickValues?: number[] | Date[] | string[] | undefined;
timeFormatLocale?: TimeLocaleDefinition;
Expand Down Expand Up @@ -1496,7 +1496,7 @@ export interface ModifiedCartesianChartProps extends CartesianChartProps {
stringDatasetForYAxisDomain?: string[];
tickParams?: {
tickValues?: number[] | Date[] | string[];
tickFormat?: string;
tickFormat?: string | ((value: number | Date) => string);
};
xAxisInnerPadding?: number;
xAxisOuterPadding?: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,16 @@ export interface CartesianChartProps {
tickValues?: number[] | Date[] | string[] | undefined;

/**
* the format for the data on x-axis. For date object this can be specified to your requirement. Eg: '%m/%d', '%d'
* Please look at https://github.com/d3/d3-time-format for all the formats supported for date axis
* Only applicable for date axis. For y-axis format use yAxisTickFormat prop.
* Format for x-axis tick labels. Accepts either a d3-format string or a `(value) => string` function.
*
* String: a d3-time-format specifier for date axes (e.g. `'%m/%d'`) or a d3-format specifier for
* numeric axes. See https://github.com/d3/d3-time-format and https://github.com/d3/d3-format.
*
* Function: formats the tick value directly — useful when a d3-format string is insufficient,
* e.g. appending a literal `%` to an already-0–100 axis (d3's `%` type multiplies by 100),
* or routing ticks through an app/i18n formatter. For y-axis format use `yAxisTickFormat`.
*/
tickFormat?: string;
tickFormat?: string | ((value: number | Date) => string);

/**
* Width of line stroke
Expand Down Expand Up @@ -644,7 +649,7 @@ export interface ModifiedCartesianChartProps extends CartesianChartProps {
*/
tickParams?: {
tickValues?: number[] | Date[] | string[];
tickFormat?: string;
tickFormat?: string | ((value: number | Date) => string);
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,16 @@ describe('createNumericXAxis', () => {
utils.createNumericXAxis(xAxisParams, {}, utils.ChartTypes.HorizontalBarChartWithAxis);
expect(xAxisParams.xAxisElement).toMatchSnapshot();
});

it('should format value-axis ticks with a function tickFormat (e.g. a literal %)', () => {
const xAxisParams = createXAxisParams();
const result = utils.createNumericXAxis(
xAxisParams,
{ tickFormat: (value: number | Date) => `${value as number}%` },
utils.ChartTypes.HorizontalBarChartWithAxis,
);
expect(result.tickLabels.every((label: string) => label.endsWith('%'))).toBe(true);
});
});

conditionalDescribe(isTimezoneSet(Timezone.UTC) && env === 'TEST')('createDateXAxis', () => {
Expand Down Expand Up @@ -274,6 +284,14 @@ conditionalDescribe(isTimezoneSet(Timezone.UTC) && env === 'TEST')('createDateXA
});
expect(xAxisParams.xAxisElement).toMatchSnapshot();
});

it('should format date-axis ticks with a function tickFormat', () => {
const xAxisParams = createXAxisParams({ domainNRangeValues });
const result = utils.createDateXAxis(xAxisParams, {
tickFormat: (value: number | Date) => `Y${(value as Date).getFullYear()}`,
});
expect(result.tickLabels.every((label: string) => label.startsWith('Y'))).toBe(true);
});
});

describe('createStringXAxis', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export interface IXAxisParams extends AxisProps {
}
export interface ITickParams {
tickValues?: Date[] | number[] | string[];
tickFormat?: string;
tickFormat?: string | ((value: number | Date) => string);
}

export interface IYAxisParams extends AxisProps {
Expand Down Expand Up @@ -288,7 +288,9 @@ export function createNumericXAxis(
return tickText[_index];
}
if (tickParams.tickFormat) {
return d3Format(tickParams.tickFormat)(domainValue);
return typeof tickParams.tickFormat === 'function'
? tickParams.tickFormat(typeof domainValue === 'number' ? domainValue : domainValue.valueOf())
: d3Format(tickParams.tickFormat)(domainValue);
Comment thread
flanakin marked this conversation as resolved.
}
const xAxisValue = typeof domainValue === 'number' ? domainValue : domainValue.valueOf();
return defaultFormat?.(xAxisValue) === '' ? '' : (formatToLocaleString(xAxisValue, culture) as string);
Expand Down Expand Up @@ -498,6 +500,9 @@ export function createDateXAxis(
if (tickParams.tickValues && tickText && typeof tickText[_index] !== 'undefined') {
return tickText[_index];
}
if (typeof tickParams.tickFormat === 'function') {
return tickParams.tickFormat(domainValue);
}
if (customDateTimeFormatter) {
return customDateTimeFormatter(domainValue);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';
import type { JSXElement } from '@fluentui/react-components';
import type { HorizontalBarChartWithAxisDataPoint } from '@fluentui/react-charts';
import { HorizontalBarChartWithAxis, getColorFromToken, DataVizPalette } from '@fluentui/react-charts';

export const HorizontalBarWithAxisFunctionTickFormat = (): JSXElement => {
const points: HorizontalBarChartWithAxisDataPoint[] = [
{ x: 10, y: 'Q1', legend: 'Completion', color: getColorFromToken(DataVizPalette.color1) },
{ x: 45, y: 'Q2', legend: 'Completion', color: getColorFromToken(DataVizPalette.color2) },
{ x: 72, y: 'Q3', legend: 'Completion', color: getColorFromToken(DataVizPalette.color3) },
{ x: 95, y: 'Q4', legend: 'Completion', color: getColorFromToken(DataVizPalette.color4) },
];

return (
<div style={{ width: '500px', height: '250px' }}>
<HorizontalBarChartWithAxis
chartTitle="Function tickFormat — literal % on a 0–100 axis"
data={points}
width={500}
height={250}
tickFormat={(value: number | Date) => `${value as number}%`}
/>
</div>
);
};

HorizontalBarWithAxisFunctionTickFormat.parameters = {
docs: {
description: {
story:
'Demonstrates passing a function to `tickFormat`. A d3-format `%` string would multiply the value by 100, ' +
'double-scaling an already-0–100 axis. A function formats the tick directly, appending a literal `%`.',
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { HorizontalBarWithAxisStringAxisTooltip } from './HorizontalBarChartWith
export { HorizontalBarWithAxisDynamic } from './HorizontalBarChartWithAxisDynamic.stories';
export { HorizontalBarWithAxisNegative } from './HorizontalBarChartWithAxisNegative.stories';
export { HorizontalBarWithAxisCategoryOrder } from './HorizontalBarChartWithAxisCategoryOrder.stories';
export { HorizontalBarWithAxisFunctionTickFormat } from './HorizontalBarChartWithAxisFunctionTickFormat.stories';

export default {
title: 'Charts/HorizontalBarChartWithAxis',
Expand Down