diff --git a/docs/data/charts/axis/FormatterD3.js b/docs/data/charts/axis/FormatterD3.js
new file mode 100644
index 0000000000000..a4d827d4b59dd
--- /dev/null
+++ b/docs/data/charts/axis/FormatterD3.js
@@ -0,0 +1,66 @@
+import * as React from 'react';
+
+import { axisClasses } from '@mui/x-charts/ChartsAxis';
+import { LineChart } from '@mui/x-charts/LineChart';
+import { ChartsReferenceLine } from '@mui/x-charts/ChartsReferenceLine';
+
+const otherSetting = {
+ height: 300,
+ grid: { horizontal: true, vertical: true },
+ sx: {
+ [`& .${axisClasses.left} .${axisClasses.label}`]: {
+ transform: 'translateX(-10px)',
+ },
+ },
+};
+
+// https://en.wikipedia.org/wiki/Low-pass_filter
+const f0 = 440;
+const frequencyResponse = (f) => 5 / Math.sqrt(1 + (f / f0) ** 2);
+
+const dataset = [
+ 0.1, 0.5, 0.8, 1, 5, 8, 10, 50, 80, 100, 500, 800, 1_000, 5_000, 8_000, 10_000,
+ 50_000, 80_000, 100_000, 500_000, 800_000, 1_000_000,
+].map((f) => ({ frequency: f, voltage: frequencyResponse(f) }));
+
+export default function FormatterD3() {
+ return (
+ {
+ if (context.location === 'tick') {
+ const d3Text = context.scale.tickFormat(30, 'e')(f);
+
+ return d3Text;
+ }
+ return `${f.toLocaleString()}Hz`;
+ },
+ },
+ ]}
+ yAxis={[
+ {
+ scaleType: 'log',
+ label: 'Vo/Vi',
+ valueFormatter: (f, context) => {
+ if (context.location === 'tick') {
+ const d3Text = context.scale.tickFormat(30, 'f')(f);
+
+ return d3Text;
+ }
+ return f.toLocaleString();
+ },
+ },
+ ]}
+ series={[{ dataKey: 'voltage' }]}
+ {...otherSetting}
+ >
+
+
+ );
+}
diff --git a/docs/data/charts/axis/FormatterD3.tsx b/docs/data/charts/axis/FormatterD3.tsx
new file mode 100644
index 0000000000000..86bbb7f228da4
--- /dev/null
+++ b/docs/data/charts/axis/FormatterD3.tsx
@@ -0,0 +1,76 @@
+import * as React from 'react';
+import { ScaleLogarithmic } from '@mui/x-charts-vendor/d3-scale';
+import { axisClasses } from '@mui/x-charts/ChartsAxis';
+import { LineChart } from '@mui/x-charts/LineChart';
+import { ChartsReferenceLine } from '@mui/x-charts/ChartsReferenceLine';
+
+const otherSetting = {
+ height: 300,
+ grid: { horizontal: true, vertical: true },
+ sx: {
+ [`& .${axisClasses.left} .${axisClasses.label}`]: {
+ transform: 'translateX(-10px)',
+ },
+ },
+};
+
+// https://en.wikipedia.org/wiki/Low-pass_filter
+const f0 = 440;
+const frequencyResponse = (f: number) => 5 / Math.sqrt(1 + (f / f0) ** 2);
+
+const dataset = [
+ 0.1, 0.5, 0.8, 1, 5, 8, 10, 50, 80, 100, 500, 800, 1_000, 5_000, 8_000, 10_000,
+ 50_000, 80_000, 100_000, 500_000, 800_000, 1_000_000,
+].map((f) => ({ frequency: f, voltage: frequencyResponse(f) }));
+
+export default function FormatterD3() {
+ return (
+ {
+ if (context.location === 'tick') {
+ const d3Text = (
+ context.scale as ScaleLogarithmic
+ ).tickFormat(
+ 30,
+ 'e',
+ )(f);
+
+ return d3Text;
+ }
+ return `${f.toLocaleString()}Hz`;
+ },
+ },
+ ]}
+ yAxis={[
+ {
+ scaleType: 'log',
+ label: 'Vo/Vi',
+ valueFormatter: (f, context) => {
+ if (context.location === 'tick') {
+ const d3Text = (
+ context.scale as ScaleLogarithmic
+ ).tickFormat(
+ 30,
+ 'f',
+ )(f);
+
+ return d3Text;
+ }
+ return f.toLocaleString();
+ },
+ },
+ ]}
+ series={[{ dataKey: 'voltage' }]}
+ {...otherSetting}
+ >
+
+
+ );
+}
diff --git a/docs/data/charts/axis/axis.md b/docs/data/charts/axis/axis.md
index ba9734f2c6d00..88193f0bf105f 100644
--- a/docs/data/charts/axis/axis.md
+++ b/docs/data/charts/axis/axis.md
@@ -70,6 +70,13 @@ To distinguish tick and tooltip, it uses the `context.location`.
{{"demo": "FormatterDemoNoSnap.js"}}
+#### Using the D3 formatter
+
+The context gives you access to the axis scale.
+The D3 [tickFormat(tickNumber, scpecifier)](https://d3js.org/d3-scale/linear#tickFormat) method can be interesting to adapt ticks format based on the scale properties.
+
+{{"demo": "FormatterD3.js"}}
+
### Axis sub domain
By default, the axis domain is computed such that all your data is visible.
diff --git a/docs/pages/x/api/charts/axis-config.json b/docs/pages/x/api/charts/axis-config.json
index d5d5f7117ac4e..3a17227050988 100644
--- a/docs/pages/x/api/charts/axis-config.json
+++ b/docs/pages/x/api/charts/axis-config.json
@@ -34,7 +34,9 @@
"default": "'extremities'"
},
"valueFormatter": {
- "type": { "description": "(value: V, context: AxisValueFormatterContext) => string" }
+ "type": {
+ "description": "<TScaleName extends S>(value: V, context: AxisValueFormatterContext<TScaleName>) => string"
+ }
},
"zoom": { "type": { "description": "boolean | ZoomOptions" }, "isProPlan": true }
}
diff --git a/packages/x-charts-pro/src/Heatmap/HeatmapTooltip.tsx b/packages/x-charts-pro/src/Heatmap/HeatmapTooltip.tsx
index 14c4acce3fbc7..a4833edfd6f2e 100644
--- a/packages/x-charts-pro/src/Heatmap/HeatmapTooltip.tsx
+++ b/packages/x-charts-pro/src/Heatmap/HeatmapTooltip.tsx
@@ -60,10 +60,12 @@ function DefaultHeatmapTooltipContent(props: Pick string;
@@ -179,6 +180,8 @@ const getText = (
}
return label?.({ value, formattedValue }) ?? formattedValue;
};
+const isZAxis = (axis: AxisDefaultized | ZAxisDefaultized): axis is ZAxisDefaultized =>
+ (axis as AxisDefaultized).scale === undefined;
const ContinuousColorLegend = consumeThemeProps(
'MuiContinuousColorLegend',
@@ -223,7 +226,8 @@ const ContinuousColorLegend = consumeThemeProps(
// Get texts to display
- const valueFormatter = (axisItem as AxisDefaultized)?.valueFormatter;
+ const valueFormatter = isZAxis(axisItem) ? undefined : axisItem.valueFormatter;
+
const formattedMin = valueFormatter
? valueFormatter(minValue, { location: 'legend' })
: minValue.toLocaleString();
diff --git a/packages/x-charts/src/ChartsLegend/PiecewiseColorLegend.tsx b/packages/x-charts/src/ChartsLegend/PiecewiseColorLegend.tsx
index c094ddf44173c..3418e943e9886 100644
--- a/packages/x-charts/src/ChartsLegend/PiecewiseColorLegend.tsx
+++ b/packages/x-charts/src/ChartsLegend/PiecewiseColorLegend.tsx
@@ -183,8 +183,9 @@ const PiecewiseColorLegend = consumeThemeProps(
return null;
}
const valueFormatter = (v: number | Date) =>
- (axisItem as AxisDefaultized).valueFormatter?.(v, { location: 'legend' }) ??
- v.toLocaleString();
+ (axisItem as AxisDefaultized).valueFormatter?.(v, {
+ location: 'legend',
+ }) ?? v.toLocaleString();
const formattedLabels = colorMap.thresholds.map(valueFormatter);
diff --git a/packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx b/packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx
index 2a239a72afa4d..905bfb356987f 100644
--- a/packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx
+++ b/packages/x-charts/src/ChartsTooltip/useAxisTooltip.tsx
@@ -123,7 +123,10 @@ export function useAxisTooltip(): UseAxisTooltipReturnValue | null {
((v: string | number | Date) =>
usedAxis.scaleType === 'utc' ? utcFormatter(v) : v.toLocaleString());
- const axisFormattedValue = axisFormatter(axisValue, { location: 'tooltip' });
+ const axisFormattedValue = axisFormatter(axisValue, {
+ location: 'tooltip',
+ scale: usedAxis.scale,
+ });
return {
axisDirection: xAxisHasData ? 'x' : 'y',
diff --git a/packages/x-charts/src/hooks/useTicks.ts b/packages/x-charts/src/hooks/useTicks.ts
index 6e9466317cb6c..a175c78e35dfb 100644
--- a/packages/x-charts/src/hooks/useTicks.ts
+++ b/packages/x-charts/src/hooks/useTicks.ts
@@ -110,7 +110,7 @@ export function useTicks(
return [
...filteredDomain.map((value) => ({
value,
- formattedValue: valueFormatter?.(value, { location: 'tick' }) ?? `${value}`,
+ formattedValue: valueFormatter?.(value, { location: 'tick', scale }) ?? `${value}`,
offset:
scale(value)! -
(scale.step() - scale.bandwidth()) / 2 +
@@ -141,7 +141,7 @@ export function useTicks(
return filteredDomain.map((value) => ({
value,
- formattedValue: valueFormatter?.(value, { location: 'tick' }) ?? `${value}`,
+ formattedValue: valueFormatter?.(value, { location: 'tick', scale }) ?? `${value}`,
offset: scale(value)!,
labelOffset: 0,
}));
@@ -158,7 +158,7 @@ export function useTicks(
return ticks.map((value: any) => ({
value,
formattedValue:
- valueFormatter?.(value, { location: 'tick' }) ?? scale.tickFormat(tickNumber)(value),
+ valueFormatter?.(value, { location: 'tick', scale }) ?? scale.tickFormat(tickNumber)(value),
offset: scale(value),
labelOffset: 0,
}));
diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts
index 821c69f6e781f..d48275d04cd73 100644
--- a/packages/x-charts/src/models/axis.ts
+++ b/packages/x-charts/src/models/axis.ts
@@ -269,15 +269,29 @@ export interface AxisScaleComputedConfig {
};
}
-export type AxisValueFormatterContext = {
- /**
- * Location indicates where the value will be displayed.
- * - `'tick'` The value is displayed on the axis ticks.
- * - `'tooltip'` The value is displayed in the tooltip when hovering the chart.
- * - `'legend'` The value is displayed in the legend when using color legend.
- */
- location: 'tick' | 'tooltip' | 'legend';
-};
+export type AxisValueFormatterContext =
+ | {
+ /**
+ * Location indicates where the value will be displayed.
+ * - `'tick'` The value is displayed on the axis ticks.
+ * - `'tooltip'` The value is displayed in the tooltip when hovering the chart.
+ * - `'legend'` The value is displayed in the legend when using color legend.
+ */
+ location: 'legend';
+ }
+ | {
+ /**
+ * Location indicates where the value will be displayed.
+ * - `'tick'` The value is displayed on the axis ticks.
+ * - `'tooltip'` The value is displayed in the tooltip when hovering the chart.
+ * - `'legend'` The value is displayed in the legend when using color legend.
+ */
+ location: 'tick' | 'tooltip';
+ /**
+ * The d3-scale instance associated to the axis.
+ */
+ scale: AxisScaleConfig[S]['scale'];
+ };
export type AxisConfig<
S extends ScaleName = ScaleName,
@@ -312,7 +326,10 @@ export type AxisConfig<
* @param {AxisValueFormatterContext} context The rendering context of the value.
* @returns {string} The string to display.
*/
- valueFormatter?: (value: V, context: AxisValueFormatterContext) => string;
+ valueFormatter?: (
+ value: V,
+ context: AxisValueFormatterContext,
+ ) => string;
/**
* If `true`, hide this value in the tooltip
*/