diff --git a/build/vega-lite-schema.json b/build/vega-lite-schema.json
index a6c9d50f57..152f30f779 100644
--- a/build/vega-lite-schema.json
+++ b/build/vega-lite-schema.json
@@ -7765,6 +7765,10 @@
"$ref": "#/definitions/TitleConfig",
"description": "Title configuration, which determines default properties for all [titles](https://vega.github.io/vega-lite/docs/title.html). For a full list of title configuration options, please see the [corresponding section of the title documentation](https://vega.github.io/vega-lite/docs/title.html#config)."
},
+ "tooltipFormat": {
+ "$ref": "#/definitions/FormatConfig",
+ "description": "Define [custom format configuration](https://vega.github.io/vega-lite/docs/config.html#format) for tooltips. If unspecified, default format config will be applied."
+ },
"trail": {
"$ref": "#/definitions/LineConfig",
"description": "Trail-Specific Config"
@@ -10851,6 +10855,36 @@
"number"
]
},
+ "FormatConfig": {
+ "additionalProperties": false,
+ "properties": {
+ "normalizedNumberFormat": {
+ "description": "If normalizedNumberFormatType is not specified, D3 number format for axis labels, text marks, and tooltips of normalized stacked fields (fields with `stack: \"normalize\"`). For example `\"s\"` for SI units. Use [D3's number format pattern](https://github.com/d3/d3-format#locale_format).\n\nIf `config.normalizedNumberFormatType` is specified and `config.customFormatTypes` is `true`, this value will be passed as `format` alongside `datum.value` to the `config.numberFormatType` function. __Default value:__ `%`",
+ "type": "string"
+ },
+ "normalizedNumberFormatType": {
+ "description": "[Custom format type](https://vega.github.io/vega-lite/docs/config.html#custom-format-type) for `config.normalizedNumberFormat`.\n\n__Default value:__ `undefined` -- This is equilvalent to call D3-format, which is exposed as [`format` in Vega-Expression](https://vega.github.io/vega/docs/expressions/#format). __Note:__ You must also set `customFormatTypes` to `true` to use this feature.",
+ "type": "string"
+ },
+ "numberFormat": {
+ "description": "If numberFormatType is not specified, D3 number format for guide labels, text marks, and tooltips of non-normalized fields (fields *without* `stack: \"normalize\"`). For example `\"s\"` for SI units. Use [D3's number format pattern](https://github.com/d3/d3-format#locale_format).\n\nIf `config.numberFormatType` is specified and `config.customFormatTypes` is `true`, this value will be passed as `format` alongside `datum.value` to the `config.numberFormatType` function.",
+ "type": "string"
+ },
+ "numberFormatType": {
+ "description": "[Custom format type](https://vega.github.io/vega-lite/docs/config.html#custom-format-type) for `config.numberFormat`.\n\n__Default value:__ `undefined` -- This is equilvalent to call D3-format, which is exposed as [`format` in Vega-Expression](https://vega.github.io/vega/docs/expressions/#format). __Note:__ You must also set `customFormatTypes` to `true` to use this feature.",
+ "type": "string"
+ },
+ "timeFormat": {
+ "description": "Default time format for raw time values (without time units) in text marks, legend labels and header labels.\n\n__Default value:__ `\"%b %d, %Y\"` __Note:__ Axes automatically determine the format for each label automatically so this config does not affect axes.",
+ "type": "string"
+ },
+ "timeFormatType": {
+ "description": "[Custom format type](https://vega.github.io/vega-lite/docs/config.html#custom-format-type) for `config.timeFormat`.\n\n__Default value:__ `undefined` -- This is equilvalent to call D3-time-format, which is exposed as [`timeFormat` in Vega-Expression](https://vega.github.io/vega/docs/expressions/#timeFormat). __Note:__ You must also set `customFormatTypes` to `true` and there must *not* be a `timeUnit` defined to use this feature.",
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
"Generator": {
"anyOf": [
{
diff --git a/examples/compiled/config_numberFormatType_tooltip.png b/examples/compiled/config_numberFormatType_tooltip.png
new file mode 100644
index 0000000000..ae559c9624
Binary files /dev/null and b/examples/compiled/config_numberFormatType_tooltip.png differ
diff --git a/examples/compiled/config_numberFormatType_tooltip.svg b/examples/compiled/config_numberFormatType_tooltip.svg
new file mode 100644
index 0000000000..069d22f072
--- /dev/null
+++ b/examples/compiled/config_numberFormatType_tooltip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/compiled/config_numberFormatType_tooltip.vg.json b/examples/compiled/config_numberFormatType_tooltip.vg.json
new file mode 100644
index 0000000000..b5c555ffc5
--- /dev/null
+++ b/examples/compiled/config_numberFormatType_tooltip.vg.json
@@ -0,0 +1,126 @@
+{
+ "$schema": "https://vega.github.io/schema/vega/v5.json",
+ "description": "Testing global number formatting config",
+ "background": "white",
+ "padding": 5,
+ "width": 150,
+ "height": 150,
+ "style": "cell",
+ "data": [
+ {
+ "name": "source_0",
+ "url": "data/cars.json",
+ "format": {"type": "json", "parse": {"Year": "date"}},
+ "transform": [
+ {
+ "type": "aggregate",
+ "groupby": ["Year"],
+ "ops": ["average"],
+ "fields": ["Miles_per_Gallon"],
+ "as": ["average_Miles_per_Gallon"]
+ },
+ {
+ "type": "filter",
+ "expr": "(isDate(datum[\"Year\"]) || (isValid(datum[\"Year\"]) && isFinite(+datum[\"Year\"]))) && isValid(datum[\"average_Miles_per_Gallon\"]) && isFinite(+datum[\"average_Miles_per_Gallon\"])"
+ }
+ ]
+ }
+ ],
+ "marks": [
+ {
+ "name": "marks",
+ "type": "rect",
+ "style": ["bar"],
+ "from": {"data": "source_0"},
+ "encode": {
+ "update": {
+ "tooltip": {
+ "signal": "{\"Year\": timeFormat(datum[\"Year\"], '%b %d, %Y'), \"Average of Miles_per_Gallon\": format(datum[\"average_Miles_per_Gallon\"], \".8f\")}"
+ },
+ "fill": {"value": "#4c78a8"},
+ "ariaRoleDescription": {"value": "bar"},
+ "description": {
+ "signal": "\"Year: \" + (timeFormat(datum[\"Year\"], '%b %d, %Y')) + \"; Average of Miles_per_Gallon: \" + (format(datum[\"average_Miles_per_Gallon\"], \".8f\"))"
+ },
+ "xc": {"scale": "x", "field": "Year"},
+ "width": {"value": 5},
+ "y": {"scale": "y", "field": "average_Miles_per_Gallon"},
+ "y2": {"scale": "y", "value": 0}
+ }
+ }
+ }
+ ],
+ "scales": [
+ {
+ "name": "x",
+ "type": "time",
+ "domain": {"data": "source_0", "field": "Year"},
+ "range": [0, {"signal": "width"}],
+ "padding": 5
+ },
+ {
+ "name": "y",
+ "type": "linear",
+ "domain": {"data": "source_0", "field": "average_Miles_per_Gallon"},
+ "range": [{"signal": "height"}, 0],
+ "nice": true,
+ "zero": true
+ }
+ ],
+ "axes": [
+ {
+ "scale": "x",
+ "orient": "bottom",
+ "gridScale": "y",
+ "grid": true,
+ "tickCount": {"signal": "ceil(width/40)"},
+ "domain": false,
+ "labels": false,
+ "aria": false,
+ "maxExtent": 0,
+ "minExtent": 0,
+ "ticks": false,
+ "zindex": 0
+ },
+ {
+ "scale": "y",
+ "orient": "left",
+ "gridScale": "x",
+ "grid": true,
+ "tickCount": {"signal": "ceil(height/40)"},
+ "tickMinStep": 1,
+ "domain": false,
+ "labels": false,
+ "aria": false,
+ "maxExtent": 0,
+ "minExtent": 0,
+ "ticks": false,
+ "zindex": 0
+ },
+ {
+ "scale": "x",
+ "orient": "bottom",
+ "grid": false,
+ "title": "Year",
+ "labelFlush": true,
+ "labelOverlap": true,
+ "tickCount": {"signal": "ceil(width/40)"},
+ "zindex": 0
+ },
+ {
+ "scale": "y",
+ "orient": "left",
+ "grid": false,
+ "title": "Average of Miles_per_Gallon",
+ "format": "d",
+ "labelOverlap": true,
+ "tickCount": {"signal": "ceil(height/40)"},
+ "tickMinStep": 1,
+ "zindex": 0
+ }
+ ],
+ "config": {
+ "tooltipFormat": {"numberFormat": ".8f"},
+ "customFormatTypes": true
+ }
+}
diff --git a/examples/specs/config_numberFormatType_tooltip.vl.json b/examples/specs/config_numberFormatType_tooltip.vl.json
new file mode 100644
index 0000000000..487f4bfae7
--- /dev/null
+++ b/examples/specs/config_numberFormatType_tooltip.vl.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
+ "description": "Testing global number formatting config",
+ "width": 150,
+ "height": 150,
+ "data": {"url": "data/cars.json"},
+ "mark": {"type": "bar", "tooltip": true},
+ "encoding": {
+ "x": {"field": "Year", "type": "temporal"},
+ "y": {"field": "Miles_per_Gallon", "type": "quantitative", "aggregate": "average"}
+ },
+ "config": {
+ "tooltipFormat": {
+ "numberFormat": ".8f"
+ },
+ "numberFormat": "d",
+ "customFormatTypes": true
+ }
+}
diff --git a/site/docs/config.md b/site/docs/config.md
index 809f275d98..0b990ec856 100644
--- a/site/docs/config.md
+++ b/site/docs/config.md
@@ -44,13 +44,13 @@ The rest of this page outlines different types of config properties:
A Vega-Lite `config` object can have the following top-level properties:
-{% include table.html props="autosize,background,countTitle,fieldTitle,font,lineBreak,padding" source="Config" %}
+{% include table.html props="autosize,background,countTitle,fieldTitle,font,lineBreak,padding,tooltipFormat" source="Config" %}
{:#format}
## Format Configuration
-These config properties define the default number and time formats for text marks as well as axes, headers, and legends:
+These config properties define the default number and time formats for text marks as well as axes, headers, tooltip, and legends:
{% include table.html props="numberFormat,numberFormatType,normalizedNumberFormat,normalizedNumberFormatType,timeFormat,timeFormatType,customFormatTypes" source="Config" %}
@@ -69,7 +69,7 @@ vega.expressionFunction('customFormatA', function(datum, params) {
});
```
-2. Setting the `customFormatTypes` config to `true`.
+(2) Setting the `customFormatTypes` config to `true`.
```js
{
@@ -78,7 +78,7 @@ vega.expressionFunction('customFormatA', function(datum, params) {
}
```
-3. You can then use this custom format function with `format` and `formatType` properties in text encodings and guides (axis/legend/header).
+(3) You can then use this custom format function with `format` and `formatType` properties in text encodings and guides (axis/legend/header).
```js
{
@@ -87,6 +87,12 @@ vega.expressionFunction('customFormatA', function(datum, params) {
}
```
+### Customize Formatter for Tooltips only
+
+Since tooltips have more screen estate and less chance of collisions, sometimes it is desirable to have a truncated format in a visualization, with a longer format in the tooltip. For example, in the visualization below, we want the y-axis to have the format `d` so it does not have a decimal point, so as not to have incredibly long labels, but on the tooltip it has the longer `.8f`. To achieve this specificity, one can add a `tooltipFormat` prop to their config that conforms to the [FormatConfig](#format) type.
+
+
+
{:#axis-config}
## Guide Configurations
diff --git a/src/compile/mark/encode/tooltip.ts b/src/compile/mark/encode/tooltip.ts
index 9b20c73f99..bf31cdd695 100644
--- a/src/compile/mark/encode/tooltip.ts
+++ b/src/compile/mark/encode/tooltip.ts
@@ -72,6 +72,7 @@ export function tooltipData(
config: Config,
{reactiveGeom}: {reactiveGeom?: boolean} = {}
) {
+ const formatConfig = {...config, ...config.tooltipFormat};
const toSkip = {};
const expr = reactiveGeom ? 'datum.datum' : 'datum';
const tuples: {channel: Channel; key: string; value: string}[] = [];
@@ -86,7 +87,7 @@ export function tooltipData(
type: (encoding[mainChannel] as TypedFieldDef).type // for secondary field def, copy type from main channel
};
- const title = fieldDef.title || defaultTitle(fieldDef, config);
+ const title = fieldDef.title || defaultTitle(fieldDef, formatConfig);
const key = array(title).join(', ');
let value: string;
@@ -99,7 +100,7 @@ export function tooltipData(
const startField = vgField(fieldDef, {expr});
const endField = vgField(fieldDef2, {expr});
const {format, formatType} = getFormatMixins(fieldDef);
- value = binFormatExpression(startField, endField, format, formatType, config);
+ value = binFormatExpression(startField, endField, format, formatType, formatConfig);
toSkip[channel2] = true;
}
}
@@ -116,12 +117,12 @@ export function tooltipData(
format,
formatType,
expr,
- config,
+ config: formatConfig,
normalizeStack: true
}).signal;
}
- value ??= textRef(fieldDef, config, expr).signal;
+ value ??= textRef(fieldDef, formatConfig, expr).signal;
tuples.push({channel, key, value});
}
diff --git a/src/config.ts b/src/config.ts
index 4c1a905e3c..e18ae6e766 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -107,43 +107,7 @@ export type ColorConfig = Record;
export type FontSizeConfig = Record;
-export interface VLOnlyConfig {
- /**
- * Default font for all text marks, titles, and labels.
- */
- font?: string;
-
- /**
- * Default color signals.
- *
- * @hidden
- */
- color?: boolean | ColorConfig;
-
- /**
- * Default font size signals.
- *
- * @hidden
- */
- fontSize?: boolean | FontSizeConfig;
-
- /**
- * Default axis and legend title for count fields.
- *
- * __Default value:__ `'Count of Records`.
- *
- * @type {string}
- */
- countTitle?: string;
-
- /**
- * Defines how Vega-Lite generates title for fields. There are three possible styles:
- * - `"verbal"` (Default) - displays function in a verbal style (e.g., "Sum of field", "Year-month of date", "field (binned)").
- * - `"function"` - displays function using parentheses and capitalized texts (e.g., "SUM(field)", "YEARMONTH(date)", "BIN(field)").
- * - `"plain"` - displays only the field name without functions (e.g., "field", "date", "field").
- */
- fieldTitle?: 'verbal' | 'functional' | 'plain';
-
+export interface FormatConfig {
/**
* If numberFormatType is not specified,
* D3 number format for guide labels, text marks, and tooltips of non-normalized fields (fields *without* `stack: "normalize"`). For example `"s"` for SI units.
@@ -197,12 +161,55 @@ export interface VLOnlyConfig {
* __Note:__ You must also set `customFormatTypes` to `true` and there must *not* be a `timeUnit` defined to use this feature.
*/
timeFormatType?: string;
+}
+
+export interface VLOnlyConfig extends FormatConfig {
+ /**
+ * Default font for all text marks, titles, and labels.
+ */
+ font?: string;
+
+ /**
+ * Default color signals.
+ *
+ * @hidden
+ */
+ color?: boolean | ColorConfig;
+
+ /**
+ * Default font size signals.
+ *
+ * @hidden
+ */
+ fontSize?: boolean | FontSizeConfig;
+
+ /**
+ * Default axis and legend title for count fields.
+ *
+ * __Default value:__ `'Count of Records`.
+ *
+ * @type {string}
+ */
+ countTitle?: string;
+
+ /**
+ * Defines how Vega-Lite generates title for fields. There are three possible styles:
+ * - `"verbal"` (Default) - displays function in a verbal style (e.g., "Sum of field", "Year-month of date", "field (binned)").
+ * - `"function"` - displays function using parentheses and capitalized texts (e.g., "SUM(field)", "YEARMONTH(date)", "BIN(field)").
+ * - `"plain"` - displays only the field name without functions (e.g., "field", "date", "field").
+ */
+ fieldTitle?: 'verbal' | 'functional' | 'plain';
/**
* Allow the `formatType` property for text marks and guides to accept a custom formatter function [registered as a Vega expression](https://vega.github.io/vega-lite/usage/compile.html#format-type).
*/
customFormatTypes?: boolean;
+ /**
+ * Define [custom format configuration](https://vega.github.io/vega-lite/docs/config.html#format) for tooltips. If unspecified, default format config will be applied.
+ */
+ tooltipFormat?: FormatConfig;
+
/** Default properties for [single view plots](https://vega.github.io/vega-lite/docs/spec.html#single). */
view?: ViewConfig;
diff --git a/test/compile/mark/encode/tooltip.test.ts b/test/compile/mark/encode/tooltip.test.ts
index 2a71bacbfe..238e7bf007 100644
--- a/test/compile/mark/encode/tooltip.test.ts
+++ b/test/compile/mark/encode/tooltip.test.ts
@@ -248,6 +248,61 @@ describe('compile/mark/encode/tooltip', () => {
});
});
+ it('returns correct tooltip signal for formatted normalized stacked field using tooltipFormat', () => {
+ expect(
+ tooltipRefForEncoding(
+ {
+ x: {
+ aggregate: 'sum',
+ field: 'IMDB_Rating',
+ type: 'quantitative'
+ }
+ },
+ {
+ fieldChannel: 'x',
+ groupbyChannels: [],
+ groupbyFields: new Set(),
+ offset: 'normalize',
+ impute: false,
+ stackBy: []
+ },
+ {...defaultConfig, tooltipFormat: {normalizedNumberFormat: '.4%'}, customFormatTypes: true}
+ )
+ ).toEqual({
+ signal: `{"Sum of IMDB_Rating": format(datum["sum_IMDB_Rating_end"]-datum["sum_IMDB_Rating_start"], ".4%")}`
+ });
+ });
+
+ it('returns correct tooltip signal for formatted normalized stacked field preferring tooltipFormat', () => {
+ expect(
+ tooltipRefForEncoding(
+ {
+ x: {
+ aggregate: 'sum',
+ field: 'IMDB_Rating',
+ type: 'quantitative'
+ }
+ },
+ {
+ fieldChannel: 'x',
+ groupbyChannels: [],
+ groupbyFields: new Set(),
+ offset: 'normalize',
+ impute: false,
+ stackBy: []
+ },
+ {
+ ...defaultConfig,
+ tooltipFormat: {normalizedNumberFormat: '.4%'},
+ normalizedNumberFormat: '.2%',
+ customFormatTypes: true
+ }
+ )
+ ).toEqual({
+ signal: `{"Sum of IMDB_Rating": format(datum["sum_IMDB_Rating_end"]-datum["sum_IMDB_Rating_start"], ".4%")}`
+ });
+ });
+
it('returns correct tooltip signal for binned field with custom title', () => {
expect(
tooltipRefForEncoding(