Skip to content

Commit

Permalink
Make tooltip support tooltip array for specifying multiple fields
Browse files Browse the repository at this point in the history
  • Loading branch information
kanitw authored and domoritz committed Apr 22, 2018
1 parent 479ce73 commit bab7b85
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 7 deletions.
66 changes: 66 additions & 0 deletions build/vega-lite-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2006,6 +2006,12 @@
},
{
"$ref": "#/definitions/TextValueDefWithCondition"
},
{
"items": {
"$ref": "#/definitions/TextFieldDef"
},
"type": "array"
}
],
"description": "The tooltip text to show upon mouse hover."
Expand Down Expand Up @@ -2211,6 +2217,12 @@
},
{
"$ref": "#/definitions/TextValueDefWithCondition"
},
{
"items": {
"$ref": "#/definitions/TextFieldDef"
},
"type": "array"
}
],
"description": "The tooltip text to show upon mouse hover."
Expand Down Expand Up @@ -6366,6 +6378,60 @@
},
"type": "object"
},
"TextFieldDef": {
"additionalProperties": false,
"properties": {
"aggregate": {
"$ref": "#/definitions/Aggregate",
"description": "Aggregation function for the field\n(e.g., `mean`, `sum`, `median`, `min`, `max`, `count`).\n\n__Default value:__ `undefined` (None)"
},
"bin": {
"anyOf": [
{
"type": "boolean"
},
{
"$ref": "#/definitions/BinParams"
}
],
"description": "A flag for binning a `quantitative` field, or [an object defining binning parameters](https://vega.github.io/vega-lite/docs/bin.html#params).\nIf `true`, default [binning parameters](https://vega.github.io/vega-lite/docs/bin.html) will be applied.\n\n__Default value:__ `false`"
},
"field": {
"anyOf": [
{
"type": "string"
},
{
"$ref": "#/definitions/RepeatRef"
}
],
"description": "__Required.__ A string defining the name of the field from which to pull a data value\nor an object defining iterated values from the [`repeat`](https://vega.github.io/vega-lite/docs/repeat.html) operator.\n\n__Note:__ Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects (e.g., `\"field\": \"foo.bar\"` and `\"field\": \"foo['bar']\"`).\nIf field names contain dots or brackets but are not nested, you can use `\\\\` to escape dots and brackets (e.g., `\"a\\\\.b\"` and `\"a\\\\[0\\\\]\"`).\nSee more details about escaping in the [field documentation](https://vega.github.io/vega-lite/docs/field.html).\n\n__Note:__ `field` is not required if `aggregate` is `count`."
},
"format": {
"description": "The [formatting pattern](https://vega.github.io/vega-lite/docs/format.html) for a text field. If not defined, this will be determined automatically.",
"type": "string"
},
"timeUnit": {
"$ref": "#/definitions/TimeUnit",
"description": "Time unit (e.g., `year`, `yearmonth`, `month`, `hours`) for a temporal field.\nor [a temporal field that gets casted as ordinal](https://vega.github.io/vega-lite/docs/type.html#cast).\n\n__Default value:__ `undefined` (None)"
},
"title": {
"description": "A title for the field. If `null`, the title will be removed.\n\n__Default value:__ derived from the field's name and transformation function (`aggregate`, `bin` and `timeUnit`). If the field has an aggregate function, the function is displayed as part of the title (e.g., `\"Sum of Profit\"`). If the field is binned or has a time unit applied, the applied function is shown in parentheses (e.g., `\"Profit (binned)\"`, `\"Transaction Date (year-month)\"`). Otherwise, the title is simply the field name.\n\n__Notes__:\n\n1) You can customize the default field title format by providing the [`fieldTitle` property in the [config](config.html) or [`fieldTitle` function via the `compile` function's options](compile.html#field-title).\n\n2) If both field definition's `title` and axis, header, or legend `title` are defined, axis/header/legend title will be used.",
"type": [
"string",
"null"
]
},
"type": {
"$ref": "#/definitions/Type",
"description": "The encoded field's type of measurement (`\"quantitative\"`, `\"temporal\"`, `\"ordinal\"`, or `\"nominal\"`).\nIt can also be a `\"geojson\"` type for encoding ['geoshape'](geoshape.html)."
}
},
"required": [
"type"
],
"type": "object"
},
"TickConfig": {
"additionalProperties": false,
"properties": {
Expand Down
2 changes: 1 addition & 1 deletion src/compile/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export function formatSignalRef(fieldDef: FieldDef<string>, specifiedFormat: str
};
} else if (fieldDef.type === 'quantitative') {
return {
signal: `${formatExpr(vgField(fieldDef, {expr}), format)}`
signal: `${formatExpr(vgField(fieldDef, {expr, binSuffix: 'range'}), format)}`
};
} else if (isTimeFieldDef(fieldDef)) {
const isUTCScale = isScaleFieldDef(fieldDef) && fieldDef['scale'] && fieldDef['scale'].type === ScaleType.UTC;
Expand Down
26 changes: 23 additions & 3 deletions src/compile/mark/mixins.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {isArray} from 'vega-util';
import {NONPOSITION_SCALE_CHANNELS} from '../../channel';
import {ChannelDef, FieldDef, getFieldDef, isConditionalSelection, isValueDef} from '../../fielddef';
import {ChannelDef, FieldDef, FieldDefWithCondition, getFieldDef, isConditionalSelection, isValueDef, TextFieldDef, ValueDefWithCondition, vgField} from '../../fielddef';
import * as log from '../../log';
import {MarkDef} from '../../mark';
import {expression} from '../../predicate';
Expand Down Expand Up @@ -96,7 +96,7 @@ export function baseEncodeEntry(model: UnitModel, ignore: Ignore) {
...markDefProperties(model.markDef, ignore),
...color(model),
...nonPosition('opacity', model),
...text(model, 'tooltip'),
...tooltip(model),
...text(model, 'href')
};
}
Expand Down Expand Up @@ -167,8 +167,28 @@ function wrapCondition(
}
}

export function text(model: UnitModel, channel: 'text' | 'tooltip' | 'href' = 'text') {
export function tooltip(model: UnitModel) {
const channel = 'tooltip';
const channelDef = model.encoding[channel];
if (isArray(channelDef)) {
const keyValues = channelDef.map((fieldDef) => {
const key = fieldDef.title !== undefined ? fieldDef.title : vgField(fieldDef, {binSuffix: 'range'});
const value = ref.text(fieldDef, model.config).signal;
return `"${key}": ${value}`;
});
return {tooltip: {signal: `{${keyValues.join(', ')}}`}};
} else {
// if not an array, behave just like text
return textCommon(model, channel, channelDef);
}
}

export function text(model: UnitModel, channel: 'text' | 'href' = 'text') {
const channelDef = model.encoding[channel];
return textCommon(model, channel, channelDef);
}

function textCommon(model: UnitModel, channel: 'text' | 'href' | 'tooltip', channelDef: FieldDefWithCondition<TextFieldDef<string>> | ValueDefWithCondition<TextFieldDef<string>>) {
return wrapCondition(model, channelDef, channel, (cDef) => ref.text(cDef, model.config));
}

Expand Down
4 changes: 2 additions & 2 deletions src/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export interface Encoding<F> {
/**
* The tooltip text to show upon mouse hover.
*/
tooltip?: FieldDefWithCondition<TextFieldDef<F>> | ValueDefWithCondition<TextFieldDef<F>>;
tooltip?: FieldDefWithCondition<TextFieldDef<F>> | ValueDefWithCondition<TextFieldDef<F>> | TextFieldDef<F>[];

/**
* A URL to load upon mouse click.
Expand Down Expand Up @@ -224,7 +224,7 @@ export function normalizeEncoding(encoding: Encoding<string>, mark: Mark): Encod
return normalizedEncoding;
}

if (channel === 'detail' || channel === 'order') {
if (channel === 'detail' || channel === 'order' || (channel === 'tooltip' && isArray(encoding[channel]))) {
const channelDef = encoding[channel];
if (channelDef) {
// Array of fieldDefs for detail channel (or production rule)
Expand Down
18 changes: 17 additions & 1 deletion test/compile/mark/mixins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import {assert} from 'chai';
import {X, Y} from '../../../src/channel';
import {color, pointPosition} from '../../../src/compile/mark/mixins';
import {color, pointPosition, tooltip} from '../../../src/compile/mark/mixins';
import * as log from '../../../src/log';
import {parseUnitModelWithScaleAndLayoutSize} from '../../util';

Expand Down Expand Up @@ -201,6 +201,22 @@ describe('compile/mark/mixins', () => {
});
});

describe('tootlip()', () => {
it('generates tooltip object signal for an array of tooltip fields', function () {
const model = parseUnitModelWithScaleAndLayoutSize({
"mark": "point",
"encoding": {
"tooltip": [
{"field": "Horsepower", "type": "quantitative"},
{"field": "Acceleration", "type": "quantitative"}
]
}
});
const props = tooltip(model);
assert.deepEqual(props.tooltip, {signal: '{"Horsepower": format(datum["Horsepower"], ""), "Acceleration": format(datum["Acceleration"], "")}'});
});
});

describe('midPoint()', function () {
it('should return correctly for lat/lng', function () {
const model = parseUnitModelWithScaleAndLayoutSize({
Expand Down

0 comments on commit bab7b85

Please sign in to comment.