Skip to content

Commit

Permalink
Href channel. Ref #1855
Browse files Browse the repository at this point in the history
  • Loading branch information
domoritz committed Jan 11, 2018
1 parent e1ec0d6 commit 0e6271c
Show file tree
Hide file tree
Showing 16 changed files with 74 additions and 17 deletions.
25 changes: 24 additions & 1 deletion build/vega-lite-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1299,6 +1299,17 @@
],
"description": "Additional levels of detail for grouping data in aggregate views and\nin line and area marks without mapping data to a specific visual channel."
},
"href": {
"anyOf": [
{
"$ref": "#/definitions/TextFieldDefWithCondition"
},
{
"$ref": "#/definitions/TextValueDefWithCondition"
}
],
"description": "A URL to load upon mouse click."
},
"opacity": {
"anyOf": [
{
Expand Down Expand Up @@ -1447,6 +1458,17 @@
],
"description": "Additional levels of detail for grouping data in aggregate views and\nin line and area marks without mapping data to a specific visual channel."
},
"href": {
"anyOf": [
{
"$ref": "#/definitions/TextFieldDefWithCondition"
},
{
"$ref": "#/definitions/TextValueDefWithCondition"
}
],
"description": "A URL to load upon mouse click."
},
"opacity": {
"anyOf": [
{
Expand Down Expand Up @@ -4508,7 +4530,8 @@
"color",
"opacity",
"text",
"tooltip"
"tooltip",
"href"
],
"type": "string"
},
Expand Down
14 changes: 7 additions & 7 deletions site/docs/encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,27 +180,27 @@ In addition to the constant `value`, [value definitions](#value-def) of mark pro
See [the `condition`](condition.html) page for examples how to specify condition logic.

{:#text}
## Text and Tooltip Channels
## Text, Tooltip, and HREF Channels

Text and tooltip channels directly encode text values of the data fields.
Text, tooltip, and HREF channels directly encode text values of the data fields.
By default, Vega-Lite automatically determines appropriate format for quantitative and temporal values. Users can set `format` property to customize text and time format.
Similar to mark property channels, definitions of text and tooltip channels can include the `condition` property to specify conditional logic.

{% include table.html props="text,tooltip" source="Encoding" %}
{% include table.html props="text,tooltip,href" source="Encoding" %}


{:#text-field-def}
### Text and Tooltip Field Definition
### Text, Tooltip, and HREF Field Definition

In addition to [`field`](field.html), [`type`](type.html), [`bin`](bin.html), [`timeUnit`](timeunit.html) and [`aggregate`](aggregate.html),
[field definitions](#field-def) for `text` and `tooltip` channels may also include these properties:
[field definitions](#field-def) for `text`, `tooltip`, and `href` channels may also include these properties:

{% include table.html props="format,condition" source="TextFieldDefWithCondition" %}

{:#text-value-def}
### Text and Tooltip Value Definition
### Text, Tooltip, and HREF Value Definition

In addition to the constant `value`, [value definitions](#value-def) of `text` and `tooltip` channels can include the `condition` property to specify conditional logic.
In addition to the constant `value`, [value definitions](#value-def) of `text`, `tooltip`, and `href` channels can include the `condition` property to specify conditional logic.

{% include table.html props="condition" source="TextValueDefWithCondition" %}

Expand Down
18 changes: 11 additions & 7 deletions src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export namespace Channel {
export const ORDER: 'order' = 'order';
export const DETAIL: 'detail' = 'detail';
export const TOOLTIP: 'tooltip' = 'tooltip';
export const HREF: 'href' = 'href';
}

export type Channel = keyof Encoding<any> | keyof FacetMapping<any>;
Expand All @@ -50,6 +51,7 @@ export const DETAIL = Channel.DETAIL;
export const ORDER = Channel.ORDER;
export const OPACITY = Channel.OPACITY;
export const TOOLTIP = Channel.TOOLTIP;
export const HREF = Channel.HREF;

const UNIT_CHANNEL_INDEX: Flag<keyof Encoding<any>> = {
x: 1,
Expand All @@ -63,7 +65,8 @@ const UNIT_CHANNEL_INDEX: Flag<keyof Encoding<any>> = {
opacity: 1,
text: 1,
detail: 1,
tooltip: 1
tooltip: 1,
href: 1,
};

const FACET_CHANNEL_INDEX: Flag<keyof FacetMapping<any>> = {
Expand Down Expand Up @@ -93,7 +96,7 @@ export const SINGLE_DEF_CHANNELS: SingleDefChannel[] = flagKeys(SINGLE_DEF_CHANN
// Using the following line leads to TypeError: Cannot read property 'elementTypes' of undefined
// when running the schema generator
// export type SingleDefChannel = typeof SINGLE_DEF_CHANNELS[0];
export type SingleDefChannel = 'x' | 'y' | 'x2' | 'y2' | 'row' | 'column' | 'size' | 'shape' | 'color' | 'opacity' | 'text' | 'tooltip';
export type SingleDefChannel = 'x' | 'y' | 'x2' | 'y2' | 'row' | 'column' | 'size' | 'shape' | 'color' | 'opacity' | 'text' | 'tooltip' | 'href';



Expand Down Expand Up @@ -124,9 +127,9 @@ export type PositionScaleChannel = typeof POSITION_SCALE_CHANNELS[0];

// NON_POSITION_SCALE_CHANNEL = SCALE_CHANNELS without X, Y
const {
// x2 and y2 share the same scale as x and y
// text and tooltip has format instead of scale
text: _t, tooltip: _tt,
// x2 and y2 share the same scale as x and y
// text, tooltip, and href has format instead of scale
text: _t, tooltip: _tt, href: _hr,
// detail and order have no scale
detail: _dd, order: _oo,
...NONPOSITION_SCALE_CHANNEL_INDEX
Expand Down Expand Up @@ -159,7 +162,6 @@ export interface SupportedMark {
line?: boolean;
area?: boolean;
text?: boolean;
tooltip?: boolean;
}

/**
Expand All @@ -184,6 +186,7 @@ export function getSupportedMark(channel: Channel): SupportedMark {
case COLOR:
case DETAIL:
case TOOLTIP:
case HREF:
case ORDER: // TODO: revise (order might not support rect, which is not stackable?)
case OPACITY:
case ROW:
Expand Down Expand Up @@ -223,9 +226,10 @@ export function rangeType(channel: Channel): RangeType {
case ROW:
case COLUMN:
case SHAPE:
// TEXT and TOOLTIP have no scale but have discrete output
// TEXT, TOOLTIP, and HREF have no scale but have discrete output
case TEXT:
case TOOLTIP:
case HREF:
return 'discrete';

// Color can be either continuous or discrete, depending on scale type.
Expand Down
1 change: 1 addition & 0 deletions src/compile/mark/area.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const area: MarkCompiler = {

...mixins.color(model),
...mixins.text(model, 'tooltip'),
...mixins.text(model, 'href'),
...mixins.nonPosition('opacity', model),
};
}
Expand Down
1 change: 1 addition & 0 deletions src/compile/mark/bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const bar: MarkCompiler = {
...y(model, stack),
...mixins.color(model),
...mixins.text(model, 'tooltip'),
...mixins.text(model, 'href'),
...mixins.nonPosition('opacity', model)
};
}
Expand Down
1 change: 1 addition & 0 deletions src/compile/mark/line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const line: MarkCompiler = {
...mixins.pointPosition('y', model, ref.mid(height)),
...mixins.color(model),
...mixins.text(model, 'tooltip'),
...mixins.text(model, 'href'),
...mixins.nonPosition('opacity', model),
...mixins.nonPosition('size', model, {
vgChannel: 'strokeWidth' // VL's line size is strokeWidth
Expand Down
2 changes: 1 addition & 1 deletion src/compile/mark/mixins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ function wrapCondition(
}
}

export function text(model: UnitModel, channel: 'text' | 'tooltip' = 'text') {
export function text(model: UnitModel, channel: 'text' | 'tooltip' | 'href' = 'text') {
const channelDef = model.encoding[channel];
return wrapCondition(model, channelDef, channel, (cDef) => ref.text(cDef, model.config));
}
Expand Down
1 change: 1 addition & 0 deletions src/compile/mark/point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function encodeEntry(model: UnitModel, fixedShape?: 'circle' | 'square') {

...mixins.color(model),
...mixins.text(model, 'tooltip'),
...mixins.text(model, 'href'),
...mixins.nonPosition('size', model),
...shapeMixins(model, config, fixedShape),
...mixins.nonPosition('opacity', model),
Expand Down
1 change: 1 addition & 0 deletions src/compile/mark/rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const rect: MarkCompiler = {
...y(model),
...mixins.color(model),
...mixins.text(model, 'tooltip'),
...mixins.text(model, 'href'),
...mixins.nonPosition('opacity', model),
};
}
Expand Down
1 change: 1 addition & 0 deletions src/compile/mark/rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const rule: MarkCompiler = {

...mixins.color(model),
...mixins.text(model, 'tooltip'),
...mixins.text(model, 'href'),
...mixins.nonPosition('opacity', model),
...mixins.nonPosition('size', model, {
vgChannel: 'strokeWidth' // VL's rule size is strokeWidth
Expand Down
1 change: 1 addition & 0 deletions src/compile/mark/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const text: MarkCompiler = {
...mixins.text(model),
...mixins.color(model),
...mixins.text(model, 'tooltip'),
...mixins.text(model, 'href'),
...mixins.nonPosition('opacity', model),
...mixins.nonPosition('size', model, {
vgChannel: 'fontSize' // VL's text size is fontSize
Expand Down
2 changes: 2 additions & 0 deletions src/compile/mark/tick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ export const tick: MarkCompiler = {
[vgThicknessChannel]: {value: config.tick.thickness},

...mixins.color(model),
...mixins.text(model, 'tooltip'),
...mixins.text(model, 'href'),
...mixins.nonPosition('opacity', model),
};
}
Expand Down
5 changes: 5 additions & 0 deletions src/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ export interface Encoding<F> {
*/
tooltip?: FieldDefWithCondition<TextFieldDef<F>> | ValueDefWithCondition<TextFieldDef<F>>;

/**
* A URL to load upon mouse click.
*/
href?: FieldDefWithCondition<TextFieldDef<F>> | ValueDefWithCondition<TextFieldDef<F>>;

/**
* Stack order for stacked marks or order of data points in line marks for connected scatter plots.
*
Expand Down
1 change: 1 addition & 0 deletions src/fielddef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ export function channelCompatibility(fieldDef: FieldDef<Field>, channel: Channel
case 'text':
case 'detail':
case 'tooltip':
case 'href':
return COMPATIBLE;

case 'opacity':
Expand Down
2 changes: 1 addition & 1 deletion test/channel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('channel', () => {

describe('SCALE_CHANNELS', () => {
it('should be UNIT_CHANNELS without X2, Y2, ORDER, DETAIL, TEXT, LABEL, TOOLTIP', () => {
assert.deepEqual(SCALE_CHANNELS, without(UNIT_CHANNELS, ['x2', 'y2', 'order', 'detail', 'text', 'label', 'tooltip']));
assert.deepEqual(SCALE_CHANNELS, without(UNIT_CHANNELS, ['x2', 'y2', 'order', 'detail', 'text', 'label', 'tooltip', 'href']));
});
});

Expand Down
15 changes: 15 additions & 0 deletions test/compile/mark/mark.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@ describe('Mark', function() {
assert.equal(markGroup[0].encode.update.tooltip.value, 'foo');
});
});

describe('Bar with href', () => {
it('should pass href value to encoding', () => {
const model = parseUnitModelWithScaleAndLayoutSize({
"mark": "bar",
"encoding": {
"x": {"type": "quantitative", "field": "Cost__Other", "aggregate": "sum"},
"y": {"bin": true, "type": "quantitative", "field": "Cost__Total_$"},
"href": {"value": "https://idl.cs.washington.edu/"}
}
});
const markGroup = parseMarkGroup(model);
assert.equal(markGroup[0].encode.update.href.value, 'https://idl.cs.washington.edu/');
});
});
});

describe('getPathSort', () => {
Expand Down

0 comments on commit 0e6271c

Please sign in to comment.