Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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(domainValue)
: 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 @@ -504,6 +506,9 @@ export function createDateXAxis(
if (timeFormatLocale) {
return formatFn(domainValue);
}
if (typeof tickParams.tickFormat === 'function') {
return tickParams.tickFormat(domainValue);
}
Comment thread
flanakin marked this conversation as resolved.
Outdated
if (culture === undefined && tickParams.tickFormat) {
if (useUTC) {
return d3UtcFormat(tickParams.tickFormat)(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