Skip to content

Commit

Permalink
Correct references to flattened fields in selection signals.
Browse files Browse the repository at this point in the history
Fields are only flattened if they participate in encoding rules.
Thus, we continue to use bracket notation to access datum values in
signal expressions as we cannot be guaranteed that the flattened field
will be present. However, the field name stored as part of a
SelectionProjection will *always* use dot notation as these names are
either returned to us flattened by model.vgField or users explicitly
specify them in the input spec (in which case, dot notation is their
only recourse). Thus, when referencing the field via a selection's
top-level signal, we just directly access the flattened field name,
rather than unpacking it via bracket notation.
  • Loading branch information
arvind committed Aug 28, 2019
1 parent d13245f commit 8837838
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 5 deletions.
4 changes: 2 additions & 2 deletions src/compile/selection/assemble.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {forEachSelection, MODIFY, SELECTION_DOMAIN, STORE, unitName, VL_SELECTIO
import {dateTimeExpr, isDateTime} from '../../datetime';
import {warn} from '../../log';
import {SelectionInit, SelectionInitInterval} from '../../selection';
import {accessPathWithDatum, keys, varName} from '../../util';
import {keys, varName} from '../../util';
import {VgData} from '../../vega.schema';
import {FacetModel} from '../facet';
import {LayerModel} from '../layer';
Expand Down Expand Up @@ -197,7 +197,7 @@ export function assembleSelectionScaleDomain(model: Model, domainRaw: SignalRef)
}
}

return {signal: accessPathWithDatum(field, name)};
return {signal: `${name}[${stringValue(field)}]`};
}

return {signal: 'null'};
Expand Down
6 changes: 3 additions & 3 deletions src/compile/selection/transforms/scales.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {VL_SELECTION_RESOLVE} from '..';
import {Channel, isScaleChannel, X, Y} from '../../../channel';
import * as log from '../../../log';
import {hasContinuousDomain} from '../../../scale';
import {accessPathWithDatum, varName} from '../../../util';
import {varName} from '../../../util';
import {UnitModel} from '../../unit';
import {SelectionProjection} from './project';
import {TransformCompiler} from './transforms';
Expand Down Expand Up @@ -33,13 +33,13 @@ const scaleBindings: TransformCompiler = {
continue;
}

scale.set('domainRaw', {signal: accessPathWithDatum(proj.field, name)}, true);
scale.set('domainRaw', {signal: `${name}[${stringValue(proj.field)}]`}, true);
bound.push(proj);

// Bind both x/y for diag plot of repeated views.
if (model.repeater && model.repeater.row === model.repeater.column) {
const scale2 = model.getScaleComponent(channel === X ? Y : X);
scale2.set('domainRaw', {signal: accessPathWithDatum(proj.field, name)}, true);
scale2.set('domainRaw', {signal: `${name}[${stringValue(proj.field)}]`}, true);
}
}
},
Expand Down
93 changes: 93 additions & 0 deletions test/compile/selection/scales.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {UnitModel} from '../../../src/compile/unit';
import * as log from '../../../src/log';
import {Domain} from '../../../src/scale';
import {parseConcatModel, parseRepeatModel, parseUnitModelWithScale} from '../../util';
import {Model} from '../../../src/compile/model';

describe('Selection + Scales', () => {
describe('domainRaw', () => {
Expand Down Expand Up @@ -159,6 +160,98 @@ describe('Selection + Scales', () => {
expect('domainRaw' in scales[0]).toBeTruthy();
expect(scales[0].domainRaw.signal).toBe('brush["date"]');
});

it('should handle nested field references', () => {
let model: Model = parseUnitModelWithScale({
selection: {
grid: {
type: 'interval',
bind: 'scales'
}
},
data: {
values: [{nested: {a: '1', b: 28}}, {nested: {a: '2', b: 55}}, {nested: {a: '3', b: 43}}]
},
mark: 'point',
encoding: {
y: {
field: 'nested.a',
type: 'quantitative'
},
x: {
field: 'nested.b',
type: 'quantitative'
}
}
});
model.parseSelections();

let scales = assembleScalesForModel(model);
expect('domainRaw' in scales[0]).toBeTruthy();
expect(scales[0].domainRaw.signal).toBe('grid["nested.b"]');
expect('domainRaw' in scales[1]).toBeTruthy();
expect(scales[1].domainRaw.signal).toBe('grid["nested.a"]');

model = parseConcatModel({
vconcat: [
{
mark: 'area',
selection: {
brush: {type: 'interval', encodings: ['x']}
},
encoding: {
x: {field: 'nested.a', type: 'temporal'},
y: {field: 'price', type: 'quantitative'}
}
},
{
mark: 'area',
encoding: {
x: {
field: 'date',
type: 'temporal',
scale: {domain: {selection: 'brush', encoding: 'x'}}
},
y: {
field: 'price',
type: 'quantitative'
}
}
},
{
mark: 'area',
encoding: {
x: {
field: 'date',
type: 'temporal',
scale: {domain: {selection: 'brush', field: 'nested.a'}}
},
y: {
field: 'price',
type: 'quantitative'
}
}
}
],
resolve: {
scale: {
color: 'independent',
opacity: 'independent'
}
}
});

model.parseScale();
model.parseSelections();

scales = assembleScalesForModel(model.children[1]);
expect('domainRaw' in scales[0]).toBeTruthy();
expect(scales[0].domainRaw.signal).toBe('brush["nested.a"]');

scales = assembleScalesForModel(model.children[2]);
expect('domainRaw' in scales[0]).toBeTruthy();
expect(scales[0].domainRaw.signal).toBe('brush["nested.a"]');
});
});

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

0 comments on commit 8837838

Please sign in to comment.