mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Vega] Allow faceted Vega-Lite charts to take correct size (#103352)
* [Vega] Allow faceted Vega-Lite charts to take correct size * Add unit test * Update autosize docs * Add warning when autosize=none Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
3f0197e323
commit
cfd836014d
4 changed files with 123 additions and 27 deletions
|
@ -23,16 +23,8 @@ Learn more about {kib} extension, additional *Vega* resources, and examples.
|
|||
====== Automatic sizing
|
||||
|
||||
Most users will want their Vega visualizations to take the full available space, so unlike
|
||||
Vega examples, `width` and `height` are not required parameters in {kib}. To set the width
|
||||
or height manually, set `autosize: none`. For example, to set the height to a specific pixel value:
|
||||
|
||||
```
|
||||
autosize: none
|
||||
width: container
|
||||
height: 200
|
||||
```
|
||||
|
||||
The default {kib} settings which are inherited by your visualizations are:
|
||||
Vega examples, `width` and `height` are not required parameters in {kib} because your
|
||||
spec will be merged with the default {kib} settings in most cases:
|
||||
|
||||
```
|
||||
autosize: {
|
||||
|
@ -43,17 +35,36 @@ width: container
|
|||
height: container
|
||||
```
|
||||
|
||||
{kib} is able to merge your custom `autosize` settings with the defaults. The options `fit-x`
|
||||
and `fit-y` are supported but not recommended over the default `fit` setting.
|
||||
These default settings are *not* applied if:
|
||||
|
||||
* <<vega-with-a-map, Your spec uses `type=map`>>
|
||||
* Your spec is Vega-Lite and contains a facet, row, column, repeat, or concat operator. In these
|
||||
cases, providing `width` and `height` will affect the child size.
|
||||
|
||||
To set the width or height manually, set `autosize: none` and provide the exact pixel sizes, including
|
||||
padding for the title, legend and axes.
|
||||
|
||||
```
|
||||
autosize: none
|
||||
width: 600
|
||||
height: 200
|
||||
padding: {
|
||||
top: 20
|
||||
bottom: 20
|
||||
left: 55
|
||||
right: 150
|
||||
}
|
||||
```
|
||||
|
||||
To learn more, read about
|
||||
https://vega.github.io/vega/docs/specification/#autosize[autosize]
|
||||
in the Vega documentation.
|
||||
https://vega.github.io/vega/docs/specification/#autosize[Vega autosize]
|
||||
and https://vega.github.io/vega-lite/docs/size.html[Vega-Lite autosize].
|
||||
|
||||
WARNING: Autosize in Vega-Lite has https://vega.github.io/vega-lite/docs/size.html#limitations[several limitations]
|
||||
that can result in a warning like `Autosize "fit" only works for single views and layered views.`
|
||||
The recommended fix for this warning is to convert your spec to Vega using the <<vega-browser-debugging-console, browser console>>
|
||||
NOTE: Autosize in Vega-Lite has https://vega.github.io/vega-lite/docs/size.html#limitations[several limitations]
|
||||
which can affect the height and width of your visualization, but these limitations do not exist in Vega.
|
||||
If you need full control, convert your spec to Vega using the <<vega-browser-debugging-console, browser console>>
|
||||
`VEGA_DEBUG.vega_spec` output.
|
||||
To disable these warnings, you can <<vega-additional-configuration-options, add extra options to your spec>>.
|
||||
|
||||
[float]
|
||||
[[vega-theme]]
|
||||
|
|
|
@ -14,6 +14,29 @@ import { bypassExternalUrlCheck } from '../vega_view/vega_base_view';
|
|||
jest.mock('../services');
|
||||
|
||||
describe(`VegaParser.parseAsync`, () => {
|
||||
function check(spec, useResize, expectedSpec, warnCount) {
|
||||
return async () => {
|
||||
const searchApiStub = {
|
||||
search: jest.fn(() => ({ toPromise: jest.fn(() => Promise.resolve({})) })),
|
||||
resetSearchStats: jest.fn(),
|
||||
};
|
||||
expectedSpec = expectedSpec || cloneDeep(spec);
|
||||
const mockGetServiceSettings = async () => {
|
||||
return {
|
||||
getFileLayers: async () => [],
|
||||
getUrlForRegionLayer: async (layer) => {
|
||||
return layer.url;
|
||||
},
|
||||
};
|
||||
};
|
||||
const vp = new VegaParser(spec, searchApiStub, 0, 0, mockGetServiceSettings);
|
||||
await vp.parseAsync();
|
||||
expect(vp.warnings).toHaveLength(warnCount || 0);
|
||||
expect(vp.useResize).toEqual(useResize);
|
||||
expect(vp.vlspec).toEqual(expectedSpec);
|
||||
};
|
||||
}
|
||||
|
||||
test(`should throw an error in case of $spec is not defined`, async () => {
|
||||
const vp = new VegaParser('{}');
|
||||
|
||||
|
@ -23,6 +46,41 @@ describe(`VegaParser.parseAsync`, () => {
|
|||
vp.error.startsWith('Your specification requires a "$schema" field with a valid URL')
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test(
|
||||
`should apply autosize on layer spec`,
|
||||
check(
|
||||
{
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v4.json',
|
||||
layer: [{ mark: 'bar' }],
|
||||
encoding: { x: { field: 'a' } },
|
||||
},
|
||||
true,
|
||||
expect.objectContaining({
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v4.json',
|
||||
layer: [{ mark: 'bar' }],
|
||||
encoding: { x: { field: 'a' } },
|
||||
autosize: { type: 'fit', contains: 'padding' },
|
||||
width: 'container',
|
||||
height: 'container',
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
test(
|
||||
`should not apply autosize on faceted spec`,
|
||||
check(
|
||||
{
|
||||
$schema: 'https://vega.github.io/schema/vega-lite/v4.json',
|
||||
mark: 'circle',
|
||||
encoding: { row: { field: 'a' } },
|
||||
},
|
||||
false,
|
||||
expect.not.objectContaining({
|
||||
autosize: { type: 'fit', contains: 'padding' },
|
||||
})
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
describe(`VegaParser._setDefaultValue`, () => {
|
||||
|
|
|
@ -14,7 +14,7 @@ import { euiPaletteColorBlind } from '@elastic/eui';
|
|||
import { euiThemeVars } from '@kbn/ui-shared-deps/theme';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { logger, Warn, version as vegaVersion } from 'vega';
|
||||
import { logger, Warn, None, version as vegaVersion } from 'vega';
|
||||
import { compile, TopLevelSpec, version as vegaLiteVersion } from 'vega-lite';
|
||||
import { EsQueryParser } from './es_query_parser';
|
||||
import { Utils } from './utils';
|
||||
|
@ -149,14 +149,14 @@ The URL is an identifier only. Kibana and your browser will never access this UR
|
|||
if (this.useMap) {
|
||||
this.mapConfig = this._parseMapConfig();
|
||||
this.useResize = false;
|
||||
} else if (this.spec) {
|
||||
this._compileWithAutosize();
|
||||
}
|
||||
|
||||
await this._resolveDataUrls();
|
||||
|
||||
if (this.isVegaLite) {
|
||||
this._compileVegaLite();
|
||||
} else {
|
||||
this._compileWithAutosize();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,6 +238,36 @@ The URL is an identifier only. Kibana and your browser will never access this UR
|
|||
* Convert VegaLite to Vega spec
|
||||
*/
|
||||
private _compileVegaLite() {
|
||||
if (!this.useMap) {
|
||||
// Compile without warnings to get the normalized spec, this simplifies the autosize detection
|
||||
const normalized = compile(this.spec as TopLevelSpec, { logger: logger(None) }).normalized;
|
||||
|
||||
// Vega-Lite allows autosize when there is a single mark or layered chart, but
|
||||
// does not allow autosize for other specs.
|
||||
if ('mark' in normalized || 'layer' in normalized) {
|
||||
this._compileWithAutosize();
|
||||
} else {
|
||||
this.useResize = false;
|
||||
if (
|
||||
normalized.autosize &&
|
||||
typeof normalized.autosize !== 'string' &&
|
||||
normalized.autosize.type === 'none'
|
||||
) {
|
||||
this._onWarning(
|
||||
i18n.translate('visTypeVega.vegaParser.widthAndHeightParamsAreRequired', {
|
||||
defaultMessage:
|
||||
'Nothing is rendered when {autoSizeParam} is set to {noneParam} while using faceted or repeated {vegaLiteParam} specs. To fix, remove {autoSizeParam} or use {vegaParam}.',
|
||||
values: {
|
||||
autoSizeParam: '"autosize"',
|
||||
noneParam: '"none"',
|
||||
vegaLiteParam: 'Vega-Lite',
|
||||
vegaParam: 'Vega',
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.vlspec = this.spec;
|
||||
const vegaLogger = logger(Warn); // note: eslint has a false positive here
|
||||
vegaLogger.warn = this._onWarning.bind(this);
|
||||
|
|
|
@ -232,18 +232,15 @@ export class VegaBaseView {
|
|||
}
|
||||
|
||||
resize() {
|
||||
if (this._parser.useResize && this._view && this.updateVegaSize(this._view)) {
|
||||
if (this._parser.useResize && this._view) {
|
||||
this.updateVegaSize(this._view);
|
||||
return this._view.runAsync();
|
||||
}
|
||||
}
|
||||
|
||||
updateVegaSize(view) {
|
||||
// For some reason the object is slightly scrollable without the extra padding.
|
||||
// This might be due to https://github.com/jquery/jquery/issues/3808
|
||||
// Which is being fixed as part of jQuery 3.3.0
|
||||
const heightExtraPadding = 6;
|
||||
const width = Math.max(0, this._$container.width());
|
||||
const height = Math.max(0, this._$container.height()) - heightExtraPadding;
|
||||
const width = Math.floor(Math.max(0, this._$container.width()));
|
||||
const height = Math.floor(Math.max(0, this._$container.height()));
|
||||
|
||||
if (view.width() !== width || view.height() !== height) {
|
||||
view.width(width).height(height);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue