Skip to content

Commit

Permalink
fix: support hiding header, setting orientation (#7150)
Browse files Browse the repository at this point in the history
Co-authored-by: GitHub Actions Bot <vega-actions-bot@users.noreply.github.com>
Co-authored-by: Dominik Moritz <domoritz@gmail.com>
  • Loading branch information
3 people committed Jan 4, 2021
1 parent fcfd955 commit d65bfff
Show file tree
Hide file tree
Showing 11 changed files with 292 additions and 22 deletions.
27 changes: 24 additions & 3 deletions build/vega-lite-schema.json
Expand Up @@ -8580,7 +8580,14 @@
"description": "__Required.__ A string defining the name of the field from which to pull a data value or an object defining iterated values from the [`repeat`](https://vega.github.io/vega-lite/docs/repeat.html) operator.\n\n__See also:__ [`field`](https://vega.github.io/vega-lite/docs/field.html) documentation.\n\n__Notes:__ 1) Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects (e.g., `\"field\": \"foo.bar\"` and `\"field\": \"foo['bar']\"`). If field names contain dots or brackets but are not nested, you can use `\\\\` to escape dots and brackets (e.g., `\"a\\\\.b\"` and `\"a\\\\[0\\\\]\"`). See more details about escaping in the [field documentation](https://vega.github.io/vega-lite/docs/field.html). 2) `field` is not required if `aggregate` is `count`."
},
"header": {
"$ref": "#/definitions/Header",
"anyOf": [
{
"$ref": "#/definitions/Header"
},
{
"type": "null"
}
],
"description": "An object defining properties of a facet's header."
},
"sort": {
Expand Down Expand Up @@ -8672,7 +8679,14 @@
"description": "__Required.__ A string defining the name of the field from which to pull a data value or an object defining iterated values from the [`repeat`](https://vega.github.io/vega-lite/docs/repeat.html) operator.\n\n__See also:__ [`field`](https://vega.github.io/vega-lite/docs/field.html) documentation.\n\n__Notes:__ 1) Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects (e.g., `\"field\": \"foo.bar\"` and `\"field\": \"foo['bar']\"`). If field names contain dots or brackets but are not nested, you can use `\\\\` to escape dots and brackets (e.g., `\"a\\\\.b\"` and `\"a\\\\[0\\\\]\"`). See more details about escaping in the [field documentation](https://vega.github.io/vega-lite/docs/field.html). 2) `field` is not required if `aggregate` is `count`."
},
"header": {
"$ref": "#/definitions/Header",
"anyOf": [
{
"$ref": "#/definitions/Header"
},
{
"type": "null"
}
],
"description": "An object defining properties of a facet's header."
},
"sort": {
Expand Down Expand Up @@ -20688,7 +20702,14 @@
"description": "__Required.__ A string defining the name of the field from which to pull a data value or an object defining iterated values from the [`repeat`](https://vega.github.io/vega-lite/docs/repeat.html) operator.\n\n__See also:__ [`field`](https://vega.github.io/vega-lite/docs/field.html) documentation.\n\n__Notes:__ 1) Dots (`.`) and brackets (`[` and `]`) can be used to access nested objects (e.g., `\"field\": \"foo.bar\"` and `\"field\": \"foo['bar']\"`). If field names contain dots or brackets but are not nested, you can use `\\\\` to escape dots and brackets (e.g., `\"a\\\\.b\"` and `\"a\\\\[0\\\\]\"`). See more details about escaping in the [field documentation](https://vega.github.io/vega-lite/docs/field.html). 2) `field` is not required if `aggregate` is `count`."
},
"header": {
"$ref": "#/definitions/Header",
"anyOf": [
{
"$ref": "#/definitions/Header"
},
{
"type": "null"
}
],
"description": "An object defining properties of a facet's header."
},
"sort": {
Expand Down
Binary file added examples/compiled/trellis_bar_no_header.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/compiled/trellis_bar_no_header.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
173 changes: 173 additions & 0 deletions examples/compiled/trellis_bar_no_header.vg.json
@@ -0,0 +1,173 @@
{
"$schema": "https://vega.github.io/schema/vega/v5.json",
"description": "A trellis bar chart showing the US population distribution of age groups and gender in 2000.",
"background": "white",
"padding": 5,
"data": [
{
"name": "source_0",
"url": "data/population.json",
"format": {"type": "json"},
"transform": [
{"type": "filter", "expr": "datum.year == 2000"},
{
"type": "formula",
"expr": "datum.sex == 2 ? 'Female' : 'Male'",
"as": "gender"
},
{
"type": "aggregate",
"groupby": ["age", "gender"],
"ops": ["sum"],
"fields": ["people"],
"as": ["sum_people"]
},
{
"type": "stack",
"groupby": ["age", "gender"],
"field": "sum_people",
"sort": {"field": ["gender"], "order": ["descending"]},
"as": ["sum_people_start", "sum_people_end"],
"offset": "zero"
},
{
"type": "filter",
"expr": "isValid(datum[\"sum_people\"]) && isFinite(+datum[\"sum_people\"])"
}
]
},
{
"name": "row_domain",
"source": "source_0",
"transform": [{"type": "aggregate", "groupby": ["gender"]}]
}
],
"signals": [
{"name": "x_step", "value": 17},
{
"name": "child_width",
"update": "bandspace(domain('x').length, 0.1, 0.05) * x_step"
},
{"name": "child_height", "value": 200}
],
"layout": {"padding": 20, "columns": 1, "bounds": "full", "align": "all"},
"marks": [
{
"name": "row_header",
"type": "group",
"role": "row-header",
"from": {"data": "row_domain"},
"sort": {"field": "datum[\"gender\"]", "order": "ascending"},
"encode": {"update": {"height": {"signal": "child_height"}}},
"axes": [
{
"scale": "y",
"orient": "left",
"grid": false,
"title": "population",
"labelOverlap": true,
"tickCount": {"signal": "ceil(child_height/40)"},
"zindex": 0
}
]
},
{
"name": "column_footer",
"type": "group",
"role": "column-footer",
"encode": {"update": {"width": {"signal": "child_width"}}},
"axes": [
{
"scale": "x",
"orient": "bottom",
"grid": false,
"title": "age",
"labelAlign": "right",
"labelAngle": 270,
"labelBaseline": "middle",
"zindex": 0
}
]
},
{
"name": "cell",
"type": "group",
"style": "cell",
"from": {
"facet": {"name": "facet", "data": "source_0", "groupby": ["gender"]}
},
"sort": {"field": ["datum[\"gender\"]"], "order": ["ascending"]},
"encode": {
"update": {
"width": {"signal": "child_width"},
"height": {"signal": "child_height"}
}
},
"marks": [
{
"name": "child_marks",
"type": "rect",
"style": ["bar"],
"from": {"data": "facet"},
"encode": {
"update": {
"fill": {"scale": "color", "field": "gender"},
"ariaRoleDescription": {"value": "bar"},
"description": {
"signal": "\"population: \" + (format(datum[\"sum_people\"], \"\")) + \"; age: \" + (isValid(datum[\"age\"]) ? datum[\"age\"] : \"\"+datum[\"age\"]) + \"; gender: \" + (isValid(datum[\"gender\"]) ? datum[\"gender\"] : \"\"+datum[\"gender\"])"
},
"x": {"scale": "x", "field": "age"},
"width": {"scale": "x", "band": 1},
"y": {"scale": "y", "field": "sum_people_end"},
"y2": {"scale": "y", "field": "sum_people_start"}
}
}
}
],
"axes": [
{
"scale": "y",
"orient": "left",
"gridScale": "x",
"grid": true,
"tickCount": {"signal": "ceil(child_height/40)"},
"domain": false,
"labels": false,
"aria": false,
"maxExtent": 0,
"minExtent": 0,
"ticks": false,
"zindex": 0
}
]
}
],
"scales": [
{
"name": "x",
"type": "band",
"domain": {"data": "source_0", "field": "age", "sort": true},
"range": {"step": {"signal": "x_step"}},
"paddingInner": 0.1,
"paddingOuter": 0.05
},
{
"name": "y",
"type": "linear",
"domain": {
"data": "source_0",
"fields": ["sum_people_start", "sum_people_end"]
},
"range": [{"signal": "child_height"}, 0],
"nice": true,
"zero": true
},
{
"name": "color",
"type": "ordinal",
"domain": {"data": "source_0", "field": "gender", "sort": true},
"range": ["#675193", "#ca8861"]
}
],
"legends": [{"fill": "color", "symbolType": "square", "title": "gender"}]
}
19 changes: 19 additions & 0 deletions examples/specs/normalized/trellis_bar_no_header_normalized.vl.json
@@ -0,0 +1,19 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"description": "A trellis bar chart showing the US population distribution of age groups and gender in 2000.",
"data": {"url": "data/population.json"},
"transform": [
{"filter": "datum.year == 2000"},
{"calculate": "datum.sex == 2 ? 'Female' : 'Male'", "as": "gender"}
],
"facet": {"row": {"field": "gender", "header": null}},
"spec": {
"width": {"step": 17},
"mark": "bar",
"encoding": {
"y": {"aggregate": "sum", "field": "people", "title": "population"},
"x": {"field": "age"},
"color": {"field": "gender", "scale": {"range": ["#675193", "#ca8861"]}}
}
}
}
23 changes: 23 additions & 0 deletions examples/specs/trellis_bar_no_header.vl.json
@@ -0,0 +1,23 @@
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"description": "A trellis bar chart showing the US population distribution of age groups and gender in 2000.",
"data": { "url": "data/population.json"},
"transform": [
{"filter": "datum.year == 2000"},
{"calculate": "datum.sex == 2 ? 'Female' : 'Male'", "as": "gender"}
],
"width": {"step": 17},
"mark": "bar",
"encoding": {
"row": {"field": "gender", "header": null},
"y": {
"aggregate": "sum", "field": "people",
"title": "population"
},
"x": {"field": "age"},
"color": {
"field": "gender",
"scale": {"range": ["#675193", "#ca8861"]}
}
}
}
22 changes: 12 additions & 10 deletions src/channeldef.ts
Expand Up @@ -1154,16 +1154,18 @@ export function initFieldDef(

if (isFacetFieldDef(fieldDef)) {
const {header} = fieldDef;
const {orient, ...rest} = header;
if (orient) {
return {
...fieldDef,
header: {
...rest,
labelOrient: header.labelOrient || orient,
titleOrient: header.titleOrient || orient
}
};
if (header) {
const {orient, ...rest} = header;
if (orient) {
return {
...fieldDef,
header: {
...rest,
labelOrient: header.labelOrient || orient,
titleOrient: header.titleOrient || orient
}
};
}
}
}

Expand Down
9 changes: 5 additions & 4 deletions src/compile/facet.ts
Expand Up @@ -79,12 +79,13 @@ export class FacetModel extends ModelWithField {
}

private initFacetFieldDef(fieldDef: FacetFieldDef<FieldName, ExprRef | SignalRef>, channel: FacetChannel) {
const {header, ...rest} = fieldDef;
// Cast because we call initFieldDef, which assumes general FieldDef.
// However, FacetFieldDef is a bit more constrained than the general FieldDef
const facetFieldDef = initFieldDef(rest, channel) as FacetFieldDef<FieldName, SignalRef>;
if (header) {
facetFieldDef.header = replaceExprRef(header);
const facetFieldDef = initFieldDef(fieldDef, channel) as FacetFieldDef<FieldName, SignalRef>;
if (facetFieldDef.header) {
facetFieldDef.header = replaceExprRef(facetFieldDef.header);
} else if (facetFieldDef.header === null) {
facetFieldDef.header = null;
}
return facetFieldDef;
}
Expand Down
8 changes: 4 additions & 4 deletions src/compile/header/parse.ts
Expand Up @@ -46,14 +46,14 @@ function parseFacetHeader(model: FacetModel, channel: FacetChannel) {
child.component.layoutHeaders[channel].title = null;
}

const labelOrient = getHeaderProperty('labelOrient', fieldDef, config, channel);
const labelOrient = getHeaderProperty('labelOrient', fieldDef.header, config, channel);

const header = fieldDef.header ?? {};
const labels = getFirstDefined(header.labels, config.header.labels, true);
const labels =
fieldDef.header !== null ? getFirstDefined(fieldDef.header?.labels, config.header.labels, true) : false;
const headerType = contains(['bottom', 'right'], labelOrient) ? 'footer' : 'header';

component.layoutHeaders[channel] = {
title,
title: fieldDef.header !== null ? title : null,
facetFieldDef: fieldDef,
[headerType]: channel === 'facet' ? [] : [makeHeaderComponent(model, channel, labels)]
};
Expand Down
2 changes: 1 addition & 1 deletion src/spec/facet.ts
Expand Up @@ -14,7 +14,7 @@ export interface FacetFieldDef<F extends Field, ES extends ExprRef | SignalRef =
/**
* An object defining properties of a facet's header.
*/
header?: Header<ES>;
header?: Header<ES> | null;

// Note: `"sort"` for facet field def is different from encoding field def as it does not support `SortByEncoding`

Expand Down
30 changes: 30 additions & 0 deletions test/compile/facet.test.ts
Expand Up @@ -43,6 +43,36 @@ describe('FacetModel', () => {
expect(localLogger.warns[0]).toEqual(log.message.facetChannelShouldBeDiscrete(ROW));
})
);

it('converts orient to titleOrient and labelOrient', () => {
const model = parseFacetModel({
facet: {
row: {field: 'a', type: 'nominal', header: {orient: 'right'}}
},
spec: {
mark: 'point',
encoding: {}
}
});
expect(model.facet).toEqual({
row: {field: 'a', type: 'nominal', header: {titleOrient: 'right', labelOrient: 'right'}}
});
});

it('keeps header: null', () => {
const model = parseFacetModel({
facet: {
row: {field: 'a', type: 'nominal', header: null}
},
spec: {
mark: 'point',
encoding: {}
}
});
expect(model.facet).toEqual({
row: {field: 'a', type: 'nominal', header: null}
});
});
});

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

0 comments on commit d65bfff

Please sign in to comment.