Skip to content

Commit

Permalink
feat: support axis labelExpr + add example with month initial
Browse files Browse the repository at this point in the history
Fix #5122
Fix #5249
  • Loading branch information
kanitw committed Aug 4, 2019
1 parent dd838c3 commit adf9e0e
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 9 deletions.
22 changes: 22 additions & 0 deletions examples/specs/bar_month_temporal_initial.vl.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v3.json",
"description": "Using `labelExpr` to show only initial letters of month names.",
"data": {"url": "data/seattle-weather.csv"},
"mark": "bar",
"encoding": {
"x": {
"timeUnit": "month",
"field": "date",
"type": "temporal",
"axis": {
"labelAlign": "left",
"labelExpr": "datum.label[0]"
}
},
"y": {
"aggregate": "mean",
"field": "precipitation",
"type": "quantitative"
}
}
}
6 changes: 5 additions & 1 deletion site/docs/encoding/axis.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,14 @@ By default, Vega-Lite automatically sets the axis extent (the space axis ticks a

### Labels

{% include table.html props="format,formatType,labels,labelAlign,labelAngle,labelBaseline,labelBound,labelColor,labelFlush,labelFlushOffset,labelFont,labelFontSize,labelFontStyle,labelFontWeight,labelLimit,labelOpacity,labelOverlap,labelPadding" source= "Axis" %}
{% include table.html props="format,formatType,labels,labelAlign,labelAngle,labelBaseline,labelBound,labelColor,labelExpr,labelFlush,labelFlushOffset,labelFont,labelFontSize,labelFontStyle,labelFontWeight,labelLimit,labelOpacity,labelOverlap,labelPadding" source= "Axis" %}

**See also:** [`guide-label` style config](mark.html#style-config) (common styles for axis, [legend](legend.html), and [header](facet.html#header) labels).

#### Example: Using Axis `labelExpr` to Display Initial Letters of Month Name

<div class="vl-example" data-name="bar_month_temporal_initial"></div>

{:#ticks}

### Ticks
Expand Down
7 changes: 7 additions & 0 deletions src/axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ export interface AxisOrientMixins {
export type AxisConfig = VlOnlyGuideConfig & AxisOrientMixins & VgAxisConfigNoSignals;

export interface Axis extends AxisOrientMixins, VgAxisConfigNoSignals, Guide {
/**
* Vega expression for customizing labels text.
* Note that the string label and value can be assessed via the `label` and `value` of the backing `datum` object.
*/
labelExpr?: string;

/**
* The offset, in pixels, by which to displace the axis from the edge of the enclosing group or data rectangle.
*
Expand Down Expand Up @@ -417,6 +423,7 @@ export const COMMON_AXIS_PROPERTIES_INDEX: Flag<keyof (VgAxis | Axis)> = {

const AXIS_PROPERTIES_INDEX: Flag<keyof Axis> = {
...COMMON_AXIS_PROPERTIES_INDEX,
labelExpr: 1,
encoding: 1
};

Expand Down
38 changes: 31 additions & 7 deletions src/compile/axis/assemble.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import {Axis as VgAxis, NewSignal} from 'vega';
import {Axis as VgAxis, AxisEncode, NewSignal} from 'vega';
import {isArray} from 'vega-util';
import {AXIS_PARTS, AXIS_PROPERTY_TYPE, CONDITIONAL_AXIS_PROP_INDEX, isConditionalAxisValue} from '../../axis';
import {POSITION_SCALE_CHANNELS} from '../../channel';
import {defaultTitle, FieldDefBase} from '../../channeldef';
import {Config} from '../../config';
import {getFirstDefined, keys} from '../../util';
import {getFirstDefined, keys, Omit} from '../../util';
import {isSignalRef, VgEncodeChannel, VgValueRef} from '../../vega.schema';
import {Model} from '../model';
import {expression} from '../predicate';
import {AxisComponent, AxisComponentIndex} from './component';
Expand All @@ -16,6 +17,18 @@ function assembleTitle(title: string | FieldDefBase<string>[], config: Config) {
return title;
}

export function setAxisEncode(
axis: Omit<VgAxis, 'orient' | 'scale'>,
part: keyof AxisEncode,
vgProp: VgEncodeChannel,
vgRef: VgValueRef | VgValueRef[]
) {
axis.encode = axis.encode || {};
axis.encode[part] = axis.encode[part] || {};
axis.encode[part].update = axis.encode[part].update || {};
axis.encode[part].update[vgProp] = vgRef;
}

export function assembleAxis(
axisCmpt: AxisComponent,
kind: 'main' | 'grid',
Expand All @@ -24,7 +37,7 @@ export function assembleAxis(
header: boolean; // whether this is called via a header
} = {header: false}
): VgAxis {
const {orient, scale, title, zindex, ...axis} = axisCmpt.combine();
const {orient, scale, labelExpr, title, zindex, ...axis} = axisCmpt.combine();

// Remove properties that are not valid for this kind of axis
keys(axis).forEach(prop => {
Expand All @@ -47,10 +60,7 @@ export function assembleAxis(
{value}
];

axis.encode = axis.encode || {};
axis.encode[part] = axis.encode[part] || {};
axis.encode[part].update = axis.encode[part].update || {};
axis.encode[part].update[vgProp] = vgRef;
setAxisEncode(axis, part, vgProp, vgRef);

delete axis[prop];
}
Expand Down Expand Up @@ -96,6 +106,20 @@ export function assembleAxis(
return undefined;
}

if (labelExpr !== undefined) {
let expr = labelExpr;
if (
axis.encode &&
axis.encode.labels &&
axis.encode.labels.update &&
isSignalRef(axis.encode.labels.update.text)
) {
expr = labelExpr.replace('datum.label', axis.encode.labels.update.text.signal);
}

setAxisEncode(axis, 'labels', 'text', {signal: expr});
}

// Remove unnecessary encode block
if (axis.encode) {
for (const part of AXIS_PARTS) {
Expand Down
2 changes: 2 additions & 0 deletions src/compile/axis/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function isFalseOrNull(v: any) {

export type AxisComponentProps = Omit<VgAxis, 'title' | ConditionalAxisProp> & {
title: string | FieldDefBase<string>[];
labelExpr: string;
} & {
[k in ConditionalAxisProp]?: BaseAxisNoSignals[k] | ConditionalAxisProperty<BaseAxisNoSignals[k]>;
};
Expand All @@ -25,6 +26,7 @@ const AXIS_COMPONENT_PROPERTIES_INDEX: Flag<keyof AxisComponentProps> = {
gridScale: 1,
scale: 1,
...COMMON_AXIS_PROPERTIES_INDEX,
labelExpr: 1,
encode: 1
};

Expand Down
2 changes: 1 addition & 1 deletion src/vega.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export type EventStream = any;

// TODO: add type of value (Make it VgValueRef<V extends ValueOrGradient> {value?:V ...})
export interface VgValueRef {
value?: ValueOrGradient;
value?: ValueOrGradient | number[];
field?:
| string
| {
Expand Down
8 changes: 8 additions & 0 deletions test/compile/axis/assemble.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,13 @@ describe('compile/axis/assemble', () => {
const axis = assembleAxis(axisCmpt, 'main', defaultConfig);
expect(axis.title).toBe('Max of a, Min of b');
});

it('correctly applies labelExpr', () => {
const axisCmpt = new AxisComponent({
labelExpr: 'datum.label[0]'
});
const axis = assembleAxis(axisCmpt, 'main', defaultConfig);
expect(axis.encode.labels.update.text).toEqual({signal: 'datum.label[0]'});
});
});
});
1 change: 1 addition & 0 deletions test/transformextract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ describe('extractTransforms()', () => {
'bar_month_band_config.vl.json',
'bar_yearmonth.vl.json',
'bar_yearmonth_custom_format.vl.json',
'bar_yearmonth_temporal_initial.vl.json',
'line_month_center_band.vl.json'
]);

Expand Down

0 comments on commit adf9e0e

Please sign in to comment.