From 86a0374744cbb0cf1b530a36d4706935b697b8e2 Mon Sep 17 00:00:00 2001 From: Kanit Wongsuphasawat Date: Wed, 25 Mar 2020 09:42:09 -0700 Subject: [PATCH] fix: fix --- src/channeldef.ts | 9 +++++-- src/compile/axis/encode.ts | 13 +++++----- src/compile/format.ts | 39 ++++++++++++++++++------------ src/compile/header/assemble.ts | 3 ++- src/compile/legend/encode.ts | 18 ++++++++------ src/compile/legend/parse.ts | 12 ++++++--- src/compile/mark/encode/text.ts | 2 +- src/compile/mark/encode/tooltip.ts | 3 ++- test/compile/format.test.ts | 2 +- 9 files changed, 62 insertions(+), 39 deletions(-) diff --git a/src/channeldef.ts b/src/channeldef.ts index f136e52726f..cf73e165f2d 100644 --- a/src/channeldef.ts +++ b/src/channeldef.ts @@ -380,7 +380,10 @@ export interface DatumDef ext /** * A constant value in data domain. */ - datum?: F extends RepeatRef ? V | RepeatRef : V; // only apply Repeatref is field (F) can be RepeatRef + datum?: F extends RepeatRef ? V | RepeatRef : V; + // only apply Repeatref is field (F) can be RepeatRef + // FIXME(https://github.com/microsoft/TypeScript/issues/37586): + // `F extends RepeatRef` probably should be `RepeatRef extends F` but there is likely a bug in TS. } export type ScaleDatumDef = ScaleMixins & DatumDef; @@ -894,7 +897,9 @@ export function getFieldDef(channelDef: ChannelDef): FieldDe return undefined; } -export function getFieldOrDatumDef(channelDef: ChannelDef): FieldDef | DatumDef { +export function getFieldOrDatumDef = ChannelDef>( + channelDef: CD +): FieldDef | DatumDef { if (isFieldOrDatumDef(channelDef)) { return channelDef; } else if (hasConditionalFieldOrDatumDef(channelDef)) { diff --git a/src/compile/axis/encode.ts b/src/compile/axis/encode.ts index e61201dd4d2..68fb010efd2 100644 --- a/src/compile/axis/encode.ts +++ b/src/compile/axis/encode.ts @@ -1,19 +1,20 @@ -import {PositionScaleChannel} from '../../channel'; +import {getSecondaryRangeChannel, PositionScaleChannel} from '../../channel'; +import {getFieldOrDatumDef} from '../../channeldef'; import {ScaleType} from '../../scale'; import {keys} from '../../util'; import {formatSignalRef} from '../format'; import {UnitModel} from '../unit'; export function labels(model: UnitModel, channel: PositionScaleChannel, specifiedLabelsSpec: any) { - const fieldDef = - model.typedFieldDef(channel) ?? - (channel === 'x' ? model.fieldDef('x2') : channel === 'y' ? model.fieldDef('y2') : undefined); + const {encoding, config} = model; + + const fieldOrDatumDef = + getFieldOrDatumDef(encoding[channel]) ?? getFieldOrDatumDef(encoding[getSecondaryRangeChannel(channel)]); const axis = model.axis(channel) || {}; const {format, formatType} = axis; - const {config} = model; const text = formatSignalRef({ - fieldDef, + fieldOrDatumDef, field: 'datum.value', format, formatType, diff --git a/src/compile/format.ts b/src/compile/format.ts index 3f52d463d65..b7b000269a5 100644 --- a/src/compile/format.ts +++ b/src/compile/format.ts @@ -2,10 +2,11 @@ import {isString} from 'vega-util'; import {isBinning} from '../bin'; import { channelDefType, + DatumDef, FieldDef, + isFieldDef, isFieldOrDatumDefForTimeFormat, isScaleFieldDef, - isTypedFieldDef, vgField } from '../channeldef'; import {Config} from '../config'; @@ -31,7 +32,7 @@ function customFormatExpr({formatType, field, format}: {formatType: string; fiel } export function formatSignalRef({ - fieldDef, + fieldOrDatumDef, format, formatType, expr, @@ -42,7 +43,7 @@ export function formatSignalRef({ omitTimeFormatConfig, isUTCScale }: { - fieldDef: FieldDef; + fieldOrDatumDef: FieldDef | DatumDef; format: string | object; formatType: string; expr?: 'datum' | 'parent' | 'datum.datum'; @@ -54,20 +55,26 @@ export function formatSignalRef({ isUTCScale?: boolean; }) { if (!field) { - if (normalizeStack) { - field = `${vgField(fieldDef, {expr, suffix: 'end'})}-${vgField(fieldDef, {expr, suffix: 'start'})}`; + if (isFieldDef(fieldOrDatumDef)) { + if (normalizeStack) { + field = `${vgField(fieldOrDatumDef, {expr, suffix: 'end'})}-${vgField(fieldOrDatumDef, { + expr, + suffix: 'start' + })}`; + } else { + field = vgField(fieldOrDatumDef, {expr}); + } } else { - field = vgField(fieldDef, {expr}); + field = `${fieldOrDatumDef.datum}`; } } - isUTCScale = - isUTCScale ?? (isScaleFieldDef(fieldDef) && fieldDef['scale'] && fieldDef['scale'].type === ScaleType.UTC); + isUTCScale = isUTCScale ?? (isScaleFieldDef(fieldOrDatumDef) && fieldOrDatumDef.scale?.type === ScaleType.UTC); const defaultTimeFormat = omitTimeFormatConfig ? null : config.timeFormat; if (isCustomFormatType(formatType)) { - if (isBinning(fieldDef.bin)) { - const endField = vgField(fieldDef, {expr, binSuffix: 'end'}); + if (isFieldDef(fieldOrDatumDef) && isBinning(fieldOrDatumDef.bin)) { + const endField = vgField(fieldOrDatumDef, {expr, binSuffix: 'end'}); return { signal: binFormatExpression(field, endField, format, formatType, config) }; @@ -77,10 +84,10 @@ export function formatSignalRef({ formatType = undefined; // drop unregistered custom formatType } - if (isFieldOrDatumDefForTimeFormat(fieldDef)) { + if (isFieldOrDatumDefForTimeFormat(fieldOrDatumDef)) { const signal = timeFormatExpression( field, - normalizeTimeUnit(fieldDef.timeUnit)?.unit, + isFieldDef(fieldOrDatumDef) ? normalizeTimeUnit(fieldOrDatumDef.timeUnit)?.unit : undefined, format, defaultTimeFormat, isUTCScale, @@ -88,13 +95,13 @@ export function formatSignalRef({ ); return signal ? {signal} : undefined; } else if (!omitNumberFormatAndEmptyTimeFormat) { - format = numberFormat(channelDefType(fieldDef), format, config); - if (isBinning(fieldDef.bin)) { - const endField = vgField(fieldDef, {expr, binSuffix: 'end'}); + format = numberFormat(channelDefType(fieldOrDatumDef), format, config); + if (isFieldDef(fieldOrDatumDef) && isBinning(fieldOrDatumDef.bin)) { + const endField = vgField(fieldOrDatumDef, {expr, binSuffix: 'end'}); return { signal: binFormatExpression(field, endField, format, formatType, config) }; - } else if (format || (isTypedFieldDef(fieldDef) && fieldDef.type === 'quantitative')) { + } else if (format || channelDefType(fieldOrDatumDef) === 'quantitative') { return { signal: `${formatExpr(field, format)}` }; diff --git a/src/compile/header/assemble.ts b/src/compile/header/assemble.ts index 13eccdf4d95..15ca0f7302e 100644 --- a/src/compile/header/assemble.ts +++ b/src/compile/header/assemble.ts @@ -124,7 +124,8 @@ export function assembleLabelTitle(facetFieldDef: FacetFieldDef, channel channel ); - const titleTextExpr = formatSignalRef({fieldDef: facetFieldDef, format, formatType, expr: 'parent', config}).signal; + const titleTextExpr = formatSignalRef({fieldOrDatumDef: facetFieldDef, format, formatType, expr: 'parent', config}) + .signal; const headerChannel = getHeaderChannel(channel, labelOrient); return { diff --git a/src/compile/legend/encode.ts b/src/compile/legend/encode.ts index b69c8b3cca1..3b179c12f95 100644 --- a/src/compile/legend/encode.ts +++ b/src/compile/legend/encode.ts @@ -3,8 +3,10 @@ import {array, isArray, stringValue} from 'vega-util'; import {COLOR, NonPositionScaleChannel, OPACITY, ScaleChannel} from '../../channel'; import { Conditional, + DatumDef, Gradient, hasConditionalValueDef, + isFieldDef, isValueDef, TypedFieldDef, Value, @@ -28,7 +30,7 @@ function type(legendCmp: LegendComponent, model: UnitModel, channel: ScaleChanne } export function symbols( - fieldDef: TypedFieldDef, + fieldOrDatumDef: TypedFieldDef | DatumDef, symbolsSpec: any, model: UnitModel, channel: ScaleChannel, @@ -47,7 +49,6 @@ export function symbols( } as SymbolEncodeEntry; // FIXME: remove this when VgEncodeEntry is compatible with SymbolEncodeEntry const opacity = getMaxValue(encoding.opacity) ?? markDef.opacity; - const condition = selectedCondition(model, legendCmp, fieldDef); if (out.fill) { // for fill legend, we don't want any fill in symbol @@ -93,6 +94,8 @@ export function symbols( } if (channel !== OPACITY) { + const condition = isFieldDef(fieldOrDatumDef) && selectedCondition(model, legendCmp, fieldOrDatumDef); + if (condition) { out.opacity = [ {test: condition, ...signalOrValueRef(opacity ?? 1)}, @@ -109,7 +112,7 @@ export function symbols( } export function gradient( - fieldDef: TypedFieldDef, + fieldOrDatumDef: TypedFieldDef | DatumDef, gradientSpec: any, model: UnitModel, channel: ScaleChannel, @@ -132,7 +135,7 @@ export function gradient( } export function labels( - fieldDef: TypedFieldDef, + fieldOrDatumDef: TypedFieldDef | DatumDef, specifiedlabelsSpec: any, model: UnitModel, channel: NonPositionScaleChannel, @@ -140,13 +143,14 @@ export function labels( ) { const legend = model.legend(channel) || {}; const config = model.config; - const condition = selectedCondition(model, legendCmp, fieldDef); + + const condition = isFieldDef(fieldOrDatumDef) ? selectedCondition(model, legendCmp, fieldOrDatumDef) : undefined; const opacity = condition ? [{test: condition, value: 1}, {value: config.legend.unselectedOpacity}] : undefined; const {format, formatType} = legend; const text = formatSignalRef({ - fieldDef, + fieldOrDatumDef, format, formatType, field: 'datum.value', @@ -165,7 +169,7 @@ export function labels( } export function entries( - fieldDef: TypedFieldDef, + fieldOrDatumDef: TypedFieldDef | DatumDef, entriesSpec: any, model: UnitModel, channel: NonPositionScaleChannel, diff --git a/src/compile/legend/parse.ts b/src/compile/legend/parse.ts index c7d66cefabe..4024b96f23d 100644 --- a/src/compile/legend/parse.ts +++ b/src/compile/legend/parse.ts @@ -102,7 +102,6 @@ function isExplicit( } export function parseLegendForChannel(model: UnitModel, channel: NonPositionScaleChannel): LegendComponent { - const fieldDef = model.fieldDef(channel); const legend = model.legend(channel); const legendCmpt = new LegendComponent({}, getLegendDefWithScale(model, channel)); @@ -111,7 +110,7 @@ export function parseLegendForChannel(model: UnitModel, channel: NonPositionScal for (const property of LEGEND_COMPONENT_PROPERTIES) { const value = getProperty(property, legend, channel, model); if (value !== undefined) { - const explicit = isExplicit(value, property, legend, fieldDef); + const explicit = isExplicit(value, property, legend, model.fieldDef(channel)); if (explicit || model.config.legend[property] === undefined) { legendCmpt.set(property, value, explicit); } @@ -123,13 +122,18 @@ export function parseLegendForChannel(model: UnitModel, channel: NonPositionScal const legendEncode = (['labels', 'legend', 'title', 'symbols', 'gradient', 'entries'] as const).reduce( (e: LegendEncode, part) => { const legendEncodingPart = guideEncodeEntry(legendEncoding[part] ?? {}, model); + + const fieldOrDatumDef = getFieldOrDatumDef(model.encoding[channel]); + const value = encode[part] - ? encode[part](fieldDef, legendEncodingPart, model, channel, legendCmpt) // apply rule + ? encode[part](fieldOrDatumDef, legendEncodingPart, model, channel, legendCmpt) // apply rule : legendEncodingPart; // no rule -- just default values if (value !== undefined && keys(value).length > 0) { e[part] = { - ...(selections?.length ? {name: `${varName(fieldDef.field)}_legend_${part}`} : {}), + ...(selections?.length && isFieldDef(fieldOrDatumDef) + ? {name: `${varName(fieldOrDatumDef.field)}_legend_${part}`} + : {}), ...(selections?.length ? {interactive: !!selections} : {}), update: value }; diff --git a/src/compile/mark/encode/text.ts b/src/compile/mark/encode/text.ts index 18aa5347f8a..c6161c09b14 100644 --- a/src/compile/mark/encode/text.ts +++ b/src/compile/mark/encode/text.ts @@ -23,7 +23,7 @@ export function textRef( } if (isTypedFieldDef(channelDef)) { const {format, formatType} = getFormatMixins(channelDef); - return formatSignalRef({fieldDef: channelDef, format, formatType, expr, config}); + return formatSignalRef({fieldOrDatumDef: channelDef, format, formatType, expr, config}); } } return undefined; diff --git a/src/compile/mark/encode/tooltip.ts b/src/compile/mark/encode/tooltip.ts index 6b66cd1a5ae..78f74fcb43e 100644 --- a/src/compile/mark/encode/tooltip.ts +++ b/src/compile/mark/encode/tooltip.ts @@ -101,7 +101,8 @@ export function tooltipRefForEncoding( toSkip[channel2] = true; } else if (stack && stack.fieldChannel === channel && stack.offset === 'normalize') { const {format, formatType} = getFormatMixins(fieldDef); - value = formatSignalRef({fieldDef, format, formatType, expr, config, normalizeStack: true}).signal; + value = formatSignalRef({fieldOrDatumDef: fieldDef, format, formatType, expr, config, normalizeStack: true}) + .signal; } } diff --git a/test/compile/format.test.ts b/test/compile/format.test.ts index 91f9b0558df..9e3e1b30c3d 100644 --- a/test/compile/format.test.ts +++ b/test/compile/format.test.ts @@ -106,7 +106,7 @@ describe('Format', () => { it('should format ordinal field defs if format is present', () => { expect( formatSignalRef({ - fieldDef: {field: 'foo', type: 'ordinal'}, + fieldOrDatumDef: {field: 'foo', type: 'ordinal'}, format: '.2f', formatType: undefined, expr: 'parent',