Skip to content

Commit

Permalink
feat: use view descriptions for accessibility (#6520)
Browse files Browse the repository at this point in the history
  • Loading branch information
domoritz committed May 21, 2020
1 parent b5c9e8a commit 60fb519
Show file tree
Hide file tree
Showing 17 changed files with 80 additions and 49 deletions.
6 changes: 3 additions & 3 deletions src/compile/axis/assemble.ts
Expand Up @@ -5,7 +5,7 @@ import {POSITION_SCALE_CHANNELS} from '../../channel';
import {defaultTitle, FieldDefBase} from '../../channeldef';
import {Config} from '../../config';
import {isText} from '../../title';
import {getFirstDefined, keys} from '../../util';
import {getFirstDefined, isEmpty} from '../../util';
import {isSignalRef, VgEncodeChannel, VgValueRef} from '../../vega.schema';
import {exprFromValueOrSignalRef} from '../common';
import {Model} from '../model';
Expand Down Expand Up @@ -116,7 +116,7 @@ export function assembleAxis(
...(grid ? {grid} : {})
};

if (keys(axis.encode).length === 0) {
if (isEmpty(axis.encode)) {
delete axis.encode;
}
}
Expand Down Expand Up @@ -159,7 +159,7 @@ export function assembleAxis(
delete axis.encode[part];
}
}
if (keys(axis.encode).length === 0) {
if (isEmpty(axis.encode)) {
delete axis.encode;
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/compile/axis/parse.ts
Expand Up @@ -2,7 +2,7 @@ import {AxisEncode as VgAxisEncode, AxisOrient} from 'vega';
import {Axis, AXIS_PARTS, isAxisProperty, isConditionalAxisValue} from '../../axis';
import {PositionScaleChannel, POSITION_SCALE_CHANNELS} from '../../channel';
import {getFieldOrDatumDef, PositionDatumDef, PositionFieldDef} from '../../channeldef';
import {getFirstDefined, keys, normalizeAngle} from '../../util';
import {getFirstDefined, isEmpty, keys, normalizeAngle} from '../../util';
import {isSignalRef} from '../../vega.schema';
import {mergeTitleComponent} from '../common';
import {guideEncodeEntry} from '../guide';
Expand Down Expand Up @@ -307,14 +307,14 @@ function parseAxis(channel: PositionScaleChannel, model: UnitModel): AxisCompone

const value = part === 'labels' ? encode.labels(model, channel, axisEncodingPart) : axisEncodingPart;

if (value !== undefined && keys(value).length > 0) {
if (value !== undefined && !isEmpty(value)) {
e[part] = {update: value};
}
return e;
}, {} as VgAxisEncode);

// FIXME: By having encode as one property, we won't have fine grained encode merging.
if (keys(axisEncode).length > 0) {
if (!isEmpty(axisEncode)) {
axisComponent.set('encode', axisEncode, !!axis.encoding || axis.labelAngle !== undefined);
}

Expand Down
4 changes: 2 additions & 2 deletions src/compile/data/bin.ts
Expand Up @@ -5,7 +5,7 @@ import {Channel} from '../../channel';
import {binRequiresRange, FieldName, isTypedFieldDef, normalizeBin, TypedFieldDef, vgField} from '../../channeldef';
import {Config} from '../../config';
import {BinTransform} from '../../transform';
import {Dict, duplicate, hash, keys, replacePathInField, unique, vals, varName} from '../../util';
import {Dict, duplicate, hash, isEmpty, keys, replacePathInField, unique, vals, varName} from '../../util';
import {binFormatExpression} from '../format';
import {isUnitModel, Model, ModelWithField} from '../model';
import {parseSelectionBinExtent} from '../selection/parse';
Expand Down Expand Up @@ -120,7 +120,7 @@ export class BinNode extends DataFlowNode {
return binComponentIndex;
}, {} as Dict<BinComponent>);

if (keys(bins).length === 0) {
if (isEmpty(bins)) {
return null;
}

Expand Down
8 changes: 4 additions & 4 deletions src/compile/data/optimizers.ts
@@ -1,21 +1,21 @@
import {MAIN, Parse} from '../../data';
import {Dict, fieldIntersection, hash, hasIntersection, keys, some} from '../../util';
import {Dict, fieldIntersection, hash, hasIntersection, isEmpty, keys, some} from '../../util';
import {Model} from '../model';
import {requiresSelectionId} from '../selection';
import {AggregateNode} from './aggregate';
import {BinNode} from './bin';
import {DataFlowNode, OutputNode} from './dataflow';
import {FacetNode} from './facet';
import {FilterNode} from './filter';
import {ParseNode} from './formatparse';
import {IdentifierNode} from './identifier';
import {JoinAggregateTransformNode} from './joinaggregate';
import {FACET_SCALE_PREFIX} from './optimize';
import {BottomUpOptimizer, isDataSourceNode, TopDownOptimizer} from './optimizer';
import * as optimizers from './optimizers';
import {StackNode} from './stack';
import {TimeUnitNode} from './timeunit';
import {WindowTransformNode} from './window';
import {IdentifierNode} from './identifier';
import {requiresSelectionId} from '../selection';

export interface OptimizerFlags {
/**
Expand Down Expand Up @@ -336,7 +336,7 @@ export class MergeParse extends BottomUpOptimizer {
delete commonParse[field];
}

if (keys(commonParse).length !== 0) {
if (!isEmpty(commonParse)) {
this.setMutated();
const mergedParseNode = new ParseNode(parent, commonParse);
for (const childNode of originalChildren) {
Expand Down
16 changes: 12 additions & 4 deletions src/compile/data/source.ts
@@ -1,7 +1,15 @@
import {Data, DataFormatType, isGenerator, isInlineData, isNamedData, isSphereGenerator, isUrlData} from '../../data';
import {contains, keys, omit} from '../../util';
import {
Data,
DataFormat,
DataFormatType,
isGenerator,
isInlineData,
isNamedData,
isSphereGenerator,
isUrlData
} from '../../data';
import {contains, isEmpty, omit} from '../../util';
import {VgData} from '../../vega.schema';
import {DataFormat} from '../../data';
import {DataFlowNode} from './dataflow';

export class SourceNode extends DataFlowNode {
Expand Down Expand Up @@ -52,7 +60,7 @@ export class SourceNode extends DataFlowNode {
this._name = data.name;
}

if (format && keys(format).length > 0) {
if (format && !isEmpty(format)) {
this._data.format = format;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/compile/data/timeunit.ts
Expand Up @@ -3,7 +3,7 @@ import {getSecondaryRangeChannel} from '../../channel';
import {hasBand, vgField} from '../../channeldef';
import {getTimeUnitParts, normalizeTimeUnit} from '../../timeunit';
import {TimeUnitTransform} from '../../transform';
import {Dict, duplicate, hash, keys, replacePathInField, vals} from '../../util';
import {Dict, duplicate, hash, isEmpty, replacePathInField, vals} from '../../util';
import {isUnitModel, ModelWithField} from '../model';
import {DataFlowNode} from './dataflow';

Expand Down Expand Up @@ -48,7 +48,7 @@ export class TimeUnitNode extends DataFlowNode {
return timeUnitComponent;
}, {} as Dict<TimeUnitComponent>);

if (keys(formula).length === 0) {
if (isEmpty(formula)) {
return null;
}

Expand Down
4 changes: 2 additions & 2 deletions src/compile/header/assemble.ts
Expand Up @@ -16,7 +16,7 @@ import {
} from '../../header';
import {isSortField} from '../../sort';
import {FacetFieldDef, isFacetMapping} from '../../spec/facet';
import {contains, keys, normalizeAngle, replaceAll} from '../../util';
import {contains, isEmpty, normalizeAngle, replaceAll} from '../../util';
import {RowCol, VgComparator, VgMarkGroup, VgTitle} from '../../vega.schema';
import {defaultLabelAlign, defaultLabelBaseline} from '../axis/properties';
import {sortArrayIndexField} from '../data/calculate';
Expand Down Expand Up @@ -253,7 +253,7 @@ export function assembleLayoutTitleBand(
}
}

return keys(titleBand).length > 0 ? titleBand : undefined;
return isEmpty(titleBand) ? undefined : titleBand;
}

export function assembleHeaderProperties(
Expand Down
8 changes: 4 additions & 4 deletions src/compile/legend/encode.ts
Expand Up @@ -13,7 +13,7 @@ import {
} from '../../channeldef';
import {Encoding} from '../../encoding';
import {FILL_STROKE_CONFIG} from '../../mark';
import {getFirstDefined, keys, varName} from '../../util';
import {getFirstDefined, isEmpty, varName} from '../../util';
import {applyMarkConfig, signalOrValueRef} from '../common';
import {formatCustomType, isCustomFormatType} from '../format';
import * as mixins from '../mark/encode';
Expand Down Expand Up @@ -122,7 +122,7 @@ export function symbols(

out = {...out, ...symbolsSpec};

return keys(out).length > 0 ? out : undefined;
return isEmpty(out) ? undefined : out;
}

export function gradient(gradientSpec: any, {model, legendType}: LegendEncodeParams) {
Expand All @@ -139,7 +139,7 @@ export function gradient(gradientSpec: any, {model, legendType}: LegendEncodePar
}

out = {...out, ...gradientSpec};
return keys(out).length > 0 ? out : undefined;
return isEmpty(out) ? undefined : out;
}

export function labels(specifiedlabelsSpec: any, {fieldOrDatumDef, model, channel, legendCmpt}: LegendEncodeParams) {
Expand Down Expand Up @@ -167,7 +167,7 @@ export function labels(specifiedlabelsSpec: any, {fieldOrDatumDef, model, channe
...specifiedlabelsSpec
};

return keys(labelsSpec).length > 0 ? labelsSpec : undefined;
return isEmpty(labelsSpec) ? undefined : labelsSpec;
}

export function entries(entriesSpec: any, {legendCmpt}: LegendEncodeParams) {
Expand Down
6 changes: 3 additions & 3 deletions src/compile/legend/parse.ts
Expand Up @@ -4,7 +4,7 @@ import {DatumDef, FieldDef, getFieldOrDatumDef, isFieldDef, MarkPropDatumDef, Ma
import {Legend, LEGEND_SCALE_CHANNELS} from '../../legend';
import {normalizeTimeUnit} from '../../timeunit';
import {GEOJSON} from '../../type';
import {deleteNestedProperty, keys, varName} from '../../util';
import {deleteNestedProperty, isEmpty, keys, varName} from '../../util';
import {mergeTitleComponent} from '../common';
import {guideEncodeEntry} from '../guide';
import {isUnitModel, Model} from '../model';
Expand Down Expand Up @@ -148,7 +148,7 @@ export function parseLegendForChannel(model: UnitModel, channel: NonPositionScal
? legendEncodeRules[part](legendEncodingPart, legendEncodeParams) // apply rule
: legendEncodingPart; // no rule -- just default values

if (value !== undefined && keys(value).length > 0) {
if (value !== undefined && !isEmpty(value)) {
legendEncode[part] = {
...(selections?.length && isFieldDef(fieldOrDatumDef)
? {name: `${varName(fieldOrDatumDef.field)}_legend_${part}`}
Expand All @@ -159,7 +159,7 @@ export function parseLegendForChannel(model: UnitModel, channel: NonPositionScal
}
}

if (keys(legendEncode).length > 0) {
if (!isEmpty(legendEncode)) {
legendCmpt.set('encode', legendEncode, !!legend?.encoding);
}

Expand Down
4 changes: 2 additions & 2 deletions src/compile/mark/encode/aria.ts
@@ -1,4 +1,4 @@
import {entries, keys} from '../../../util';
import {entries, isEmpty} from '../../../util';
import {getMarkPropOrConfig, signalOrValueRef} from '../../common';
import {VG_MARK_INDEX} from './../../../vega.schema';
import {UnitModel} from './../../unit';
Expand Down Expand Up @@ -63,7 +63,7 @@ export function description(model: UnitModel) {

const data = tooltipData(encoding, stack, config);

if (keys(data).length === 0) {
if (isEmpty(data)) {
return undefined;
}

Expand Down
17 changes: 12 additions & 5 deletions src/compile/model.ts
Expand Up @@ -34,7 +34,7 @@ import {
import {NormalizedSpec} from '../spec/index';
import {extractTitleConfig, isText, TitleParams} from '../title';
import {normalizeTransform, Transform} from '../transform';
import {contains, Dict, duplicate, keys, varName} from '../util';
import {contains, Dict, duplicate, keys, varName, isEmpty} from '../util';
import {isVgRangeStep, VgData, VgEncodeEntry, VgLayout, VgMarkGroup} from '../vega.schema';
import {assembleAxes} from './axis/assemble';
import {AxisComponentIndex} from './axis/component';
Expand Down Expand Up @@ -307,22 +307,29 @@ export abstract class Model {
// Exclude "style"
const {style: _, ...baseView} = view;

const e = {};
const e: VgEncodeEntry = {};
for (const property of keys(baseView)) {
const value = baseView[property];
if (value !== undefined) {
e[property] = signalOrValueRef(value);
}
}

return e;
}

public assembleGroupEncodeEntry(isTopLevel: boolean): VgEncodeEntry {
let encodeEntry: VgEncodeEntry = undefined;
let encodeEntry: VgEncodeEntry = {};
if (this.view) {
encodeEntry = this.assembleEncodeFromView(this.view);
}

if (!isTopLevel) {
// Descriptions are already added to the top-level description so we only need to add them to the inner views.
if (this.description) {
encodeEntry['description'] = signalOrValueRef(this.description);
}

// For top-level spec, we can set the global width and height signal to adjust the group size.
// For other child specs, we have to manually set width and height in the encode entry.
if (this.type === 'unit' || this.type === 'layer') {
Expand All @@ -334,7 +341,7 @@ export abstract class Model {
}
}

return encodeEntry;
return isEmpty(encodeEntry) ? undefined : encodeEntry;
}

public assembleLayout(): VgLayout {
Expand Down Expand Up @@ -414,7 +421,7 @@ export abstract class Model {
title.anchor = title.anchor ?? 'start';
}

return keys(title).length > 0 ? title : undefined;
return isEmpty(title) ? undefined : title;
}
return undefined;
}
Expand Down
4 changes: 2 additions & 2 deletions src/compile/selection/transforms/project.ts
Expand Up @@ -3,7 +3,7 @@ import {isSingleDefUnitChannel, ScaleChannel, SingleDefUnitChannel} from '../../
import * as log from '../../../log';
import {hasContinuousDomain} from '../../../scale';
import {SelectionInit, SelectionInitInterval} from '../../../selection';
import {Dict, hash, keys, replacePathInField, varName} from '../../../util';
import {Dict, hash, keys, replacePathInField, varName, isEmpty} from '../../../util';
import {TimeUnitComponent, TimeUnitNode} from '../../data/timeunit';
import {TransformCompiler} from './transforms';

Expand Down Expand Up @@ -165,7 +165,7 @@ const project: TransformCompiler = {
}
}

if (keys(timeUnits).length > 0) {
if (!isEmpty(timeUnits)) {
proj.timeUnit = new TimeUnitNode(null, timeUnits);
}
},
Expand Down
14 changes: 7 additions & 7 deletions src/config.ts
Expand Up @@ -21,7 +21,7 @@ import {defaultConfig as defaultSelectionConfig, SelectionConfig} from './select
import {BaseViewBackground, CompositionConfigMixins, DEFAULT_SPACING, isStep} from './spec/base';
import {TopLevelProperties} from './spec/toplevel';
import {extractTitleConfig, TitleConfig} from './title';
import {duplicate, getFirstDefined, keys} from './util';
import {duplicate, getFirstDefined, isEmpty} from './util';

export interface ViewConfig extends BaseViewBackground {
/**
Expand Down Expand Up @@ -574,12 +574,12 @@ export function stripAndRedirectConfig(config: Config) {

// Remove empty config objects.
for (const prop in config) {
if (isObject(config[prop]) && keys(config[prop]).length === 0) {
if (isObject(config[prop]) && isEmpty(config[prop])) {
delete config[prop];
}
}

return keys(config).length > 0 ? config : undefined;
return isEmpty(config) ? undefined : config;
}

/**
Expand All @@ -593,21 +593,21 @@ function redirectTitleConfig(config: Config) {
const {titleMarkConfig, subtitleMarkConfig, subtitle} = extractTitleConfig(config.title);

// set config.style if title/subtitleMarkConfig is not an empty object
if (keys(titleMarkConfig).length > 0) {
if (!isEmpty(titleMarkConfig)) {
config.style['group-title'] = {
...config.style['group-title'],
...titleMarkConfig // config.title has higher precedence than config.style.group-title in Vega
};
}
if (keys(subtitleMarkConfig).length > 0) {
if (!isEmpty(subtitleMarkConfig)) {
config.style['group-subtitle'] = {
...config.style['group-subtitle'],
...subtitleMarkConfig
};
}

// subtitle part can stay in config.title since header titles do not use subtitle
if (keys(subtitle).length > 0) {
if (!isEmpty(subtitle)) {
config.title = subtitle;
} else {
delete config.title;
Expand All @@ -632,7 +632,7 @@ function redirectConfigToStyleConfig(
};

// set config.style if it is not an empty object
if (keys(style).length > 0) {
if (!isEmpty(style)) {
config.style[toProp ?? prop] = style;
}

Expand Down
4 changes: 2 additions & 2 deletions src/normalize/core.ts
Expand Up @@ -23,7 +23,7 @@ import {NormalizedLayerSpec} from '../spec/layer';
import {SpecMapper} from '../spec/map';
import {isLayerRepeatSpec, LayerRepeatSpec, NonLayerRepeatSpec, RepeatSpec} from '../spec/repeat';
import {isUnitSpec, NormalizedUnitSpec} from '../spec/unit';
import {keys, omit, varName} from '../util';
import {keys, omit, varName, isEmpty} from '../util';
import {NonFacetUnitNormalizer, NormalizerParams} from './base';
import {PathOverlayNormalizer} from './pathoverlay';
import {RangeStepNormalizer} from './rangestep';
Expand Down Expand Up @@ -344,7 +344,7 @@ function mergeEncoding(opt: {parentEncoding: Encoding<any>; encoding: Encoding<a
...(parentEncoding ?? {}),
...(encoding ?? {})
};
return keys(merged).length > 0 ? merged : undefined;
return isEmpty(merged) ? undefined : merged;
}

function mergeProjection(opt: {parentProjection: Projection; projection: Projection}) {
Expand Down

0 comments on commit 60fb519

Please sign in to comment.