diff --git a/src/compile/axis/parse.ts b/src/compile/axis/parse.ts index 36c83070e5d..e4cf3d6e440 100644 --- a/src/compile/axis/parse.ts +++ b/src/compile/axis/parse.ts @@ -1,6 +1,6 @@ import {Axis, AXIS_PARTS, AxisEncoding, isAxisProperty, VG_AXIS_PROPERTIES} from '../../axis'; import {POSITION_SCALE_CHANNELS, PositionScaleChannel, X, Y} from '../../channel'; -import {FieldDefBase, toFieldDefBase} from '../../fielddef'; +import {FieldDefBase, title, toFieldDefBase} from '../../fielddef'; import {keys} from '../../util'; import {AxisOrient, VgAxis, VgAxisEncode} from '../../vega.schema'; import {getSpecifiedOrDefaultValue, mergeTitleFieldDefs, numberFormat, titleMerger} from '../common'; @@ -152,6 +152,25 @@ function mergeAxisComponent(merged: AxisComponent, child: AxisComponent): AxisCo return merged; } +function getFieldDefTitle(model: UnitModel, channel: 'x' | 'y') { + const channel2 = channel === 'x' ? 'x2' : 'y2'; + const fieldDef = model.fieldDef(channel); + const fieldDef2 = model.fieldDef(channel2); + + const titles = [ + ...(fieldDef && fieldDef.title ? [fieldDef.title] : []), + ...(fieldDef2 && fieldDef2.title ? [fieldDef2.title] : []) + ]; + + if (titles.length > 0) { + return titles.join(', '); + } else if (fieldDef && fieldDef.title !== undefined) { // explicit falsy value + return fieldDef.title; + } else if (fieldDef2 && fieldDef2.title !== undefined) { // explicit falsy value + return fieldDef2.title; + } + return undefined; +} function parseAxis(channel: PositionScaleChannel, model: UnitModel): AxisComponent { const axis = model.axis(channel); @@ -168,7 +187,7 @@ function parseAxis(channel: PositionScaleChannel, model: UnitModel): AxisCompone // both VL axis.encoding and axis.labelAngle affect VG axis.encode property === 'encode' ? !!axis.encoding || !!axis.labelAngle : // title can be explicit if fieldDef.title is set - property === 'title' && value === model.fieldDef(channel).title ? true : + property === 'title' && value === getFieldDefTitle(model, channel) ? true : // Otherwise, things are explicit if the returned value matches the specified property value === axis[property]; @@ -242,7 +261,8 @@ function getProperty(property: K, specifiedA const fieldDef2 = model.fieldDef(channel2); // Keep undefined so we use default if title is unspecified. // For other falsy value, keep them so we will hide the title. - const specifiedTitle = fieldDef.title !== undefined ? fieldDef.title : + const fieldDefTitle = getFieldDefTitle(model, channel); + const specifiedTitle = fieldDefTitle !== undefined ? fieldDefTitle : specifiedAxis.title === undefined ? undefined : specifiedAxis.title; return getSpecifiedOrDefaultValue[]>( diff --git a/test/compile/axis/parse.test.ts b/test/compile/axis/parse.test.ts index 2bdc022e54f..5f3c516c241 100644 --- a/test/compile/axis/parse.test.ts +++ b/test/compile/axis/parse.test.ts @@ -129,6 +129,25 @@ describe('Axis', function() { assert.equal(axisComponent['x'][0].explicit.title, val as any); } }); + it('should store the fieldDef title value for x, x2 if title = null, "", or false', function () { + for (const channel of ['x', 'x2']) { + for (const val of [null, '', false]) { + const model = parseUnitModelWithScale({ + mark: "point", + encoding: { + [channel]: { + field: "a", + type: "quantitative", + title: val as any // Need to cast as false is not valid, but we want to fall back gracefully + } + } + }); + const axisComponent = parseUnitAxis(model); + assert.equal(axisComponent.x.length, 1); + assert.equal(axisComponent.x[0].explicit.title, val as any); + } + } + }); it('should store fieldDef.title as explicit', function () { const model = parseUnitModelWithScale({ @@ -146,6 +165,27 @@ describe('Axis', function() { assert.equal(axisComponent['x'][0].explicit.title, 'foo'); }); + it('should merge title of fieldDef and fieldDef2', function () { + const model = parseUnitModelWithScale({ + mark: "bar", + encoding: { + x: { + field: "a", + type: "quantitative", + title: 'foo' + }, + x2: { + field: "b", + type: "quantitative", + title: 'bar' + } + } + }); + const axisComponent = parseUnitAxis(model); + assert.equal(axisComponent['x'].length, 1); + assert.equal(axisComponent['x'][0].explicit.title, 'foo, bar'); + }); + it('should store both x and x2 for ranged mark', function () { const model = parseUnitModelWithScale({ mark: "rule",