[Step 3] VisEditors Telemetry enhancements (add new agg-based and lens telemetries) (#135615)

* initial comit

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* push chart_expressions logic

* update tests

* fix JEST

* push some telemetries

* fix some cases

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* update tests

* add some lens part

* add handlers.logRenderTelemetry method

* visGroup -> originatingApp

* remove visTpe, extra, onlyExtra

* remove handlers.logRenderTelemetry from handlers

* remove context from snapshots

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* add lens operations telemetry

* fix heaatmap, vislib

* push some telemetries

* cleanup

* push some logic

* fix merge conflicts

* push some logic

* add lens map telemetry

* add render_lens_vis_cases

* add render_lens_vis_observability_exploratory_view

* cleanup

* cleanup

* make getRenderEventCounters optional

* add summary_row and color_by_value telemetries

* try to fix double rendering

* update xy telemetries

* fix TSVB

* fix lens

* fix Timelion

* add mixed_xy telemetry

* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'

* Update x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx

Co-authored-by: Shahzad <shahzad31comp@gmail.com>

* Update x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx

Co-authored-by: Shahzad <shahzad31comp@gmail.com>

* Update expression_renderer.tsx

* update originatingApp

* Update expression_renderer.tsx

* add JEST for core changes

* Update plugin.ts

* Update src/plugins/expressions/common/expression_renderers/types.ts

Co-authored-by: Michael Dokolin <dokmic@gmail.com>

* fix PR comments

* add renderComplete param to VisualizationContainer

* fix mixed_xy issue

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Shahzad <shahzad31comp@gmail.com>
Co-authored-by: Michael Dokolin <dokmic@gmail.com>
This commit is contained in:
Alexey Antonov 2022-07-25 15:33:10 +03:00 committed by GitHub
parent 7a153139cf
commit d4bb959bea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
120 changed files with 1566 additions and 415 deletions

View file

@ -18,13 +18,16 @@ describe('KibanaExecutionContext', () => {
type: 'test-type',
name: 'test-name',
id: '42',
meta: {
foo: 'test',
},
description: 'test-descripton',
};
const value = new ExecutionContextContainer(context).toHeader();
expect(value).toMatchInlineSnapshot(`
Object {
"x-kbn-context": "%7B%22type%22%3A%22test-type%22%2C%22name%22%3A%22test-name%22%2C%22id%22%3A%2242%22%2C%22description%22%3A%22test-descripton%22%7D",
"x-kbn-context": "%7B%22type%22%3A%22test-type%22%2C%22name%22%3A%22test-name%22%2C%22id%22%3A%2242%22%2C%22meta%22%3A%7B%22foo%22%3A%22test%22%7D%2C%22description%22%3A%22test-descripton%22%7D",
}
`);
});
@ -34,6 +37,9 @@ describe('KibanaExecutionContext', () => {
type: 'child-test-type',
name: 'child-test-name',
id: '42',
meta: {
foo: true,
},
description: 'child-test-descripton',
};
@ -48,7 +54,7 @@ describe('KibanaExecutionContext', () => {
const value = new ExecutionContextContainer(context).toHeader();
expect(value).toMatchInlineSnapshot(`
Object {
"x-kbn-context": "%7B%22type%22%3A%22type%22%2C%22name%22%3A%22name%22%2C%22id%22%3A%2241%22%2C%22description%22%3A%22descripton%22%2C%22child%22%3A%7B%22type%22%3A%22child-test-type%22%2C%22name%22%3A%22child-test-name%22%2C%22id%22%3A%2242%22%2C%22description%22%3A%22child-test-descripton%22%7D%7D",
"x-kbn-context": "%7B%22type%22%3A%22type%22%2C%22name%22%3A%22name%22%2C%22id%22%3A%2241%22%2C%22description%22%3A%22descripton%22%2C%22child%22%3A%7B%22type%22%3A%22child-test-type%22%2C%22name%22%3A%22child-test-name%22%2C%22id%22%3A%2242%22%2C%22meta%22%3A%7B%22foo%22%3Atrue%7D%2C%22description%22%3A%22child-test-descripton%22%7D%7D",
}
`);
});
@ -58,13 +64,16 @@ describe('KibanaExecutionContext', () => {
type: 'test-type',
name: 'test-name',
id: '42',
meta: {
foo: '1',
},
description: 'long long test-descripton,'.repeat(1000),
};
const value = new ExecutionContextContainer(context).toHeader();
expect(value).toMatchInlineSnapshot(`
Object {
"x-kbn-context": "%7B%22type%22%3A%22test-type%22%2C%22name%22%3A%22test-name%22%2C%22id%22%3A%2242%22%2C%22description%22%3A%22long%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test",
"x-kbn-context": "%7B%22type%22%3A%22test-type%22%2C%22name%22%3A%22test-name%22%2C%22id%22%3A%2242%22%2C%22meta%22%3A%7B%22foo%22%3A%221%22%7D%2C%22description%22%3A%22long%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20long%20test-descripton%2Clong%20lo",
}
`);
@ -78,13 +87,16 @@ describe('KibanaExecutionContext', () => {
type: 'test-type',
name: 'test-name',
id: '42',
meta: {
foo: 'meta',
},
description: 'описание',
};
const value = new ExecutionContextContainer(context).toHeader();
expect(value).toMatchInlineSnapshot(`
Object {
"x-kbn-context": "%7B%22type%22%3A%22test-type%22%2C%22name%22%3A%22test-name%22%2C%22id%22%3A%2242%22%2C%22description%22%3A%22%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5%22%7D",
"x-kbn-context": "%7B%22type%22%3A%22test-type%22%2C%22name%22%3A%22test-name%22%2C%22id%22%3A%2242%22%2C%22meta%22%3A%7B%22foo%22%3A%22meta%22%7D%2C%22description%22%3A%22%D0%BE%D0%BF%D0%B8%D1%81%D0%B0%D0%BD%D0%B8%D0%B5%22%7D",
}
`);
});
@ -95,6 +107,9 @@ describe('KibanaExecutionContext', () => {
type: 'test-type',
name: 'test-name',
id: '42',
meta: {
foo: false,
},
description: 'test-descripton',
};
@ -107,6 +122,9 @@ describe('KibanaExecutionContext', () => {
type: 'child-b-type',
name: 'child-b-name',
id: '42',
meta: {
foo: 'test',
},
description: 'child-b-descripton',
};

View file

@ -32,57 +32,103 @@ describe('ExecutionContextService', () => {
const context$ = analytics.registerContextProvider.mock.calls[0][0].context$;
execContext.set({
type: 'ghf',
meta: {
foo: 'test',
},
description: 'first set',
});
await expect(firstValueFrom(context$)).resolves.toMatchInlineSnapshot(`
Object {
"applicationId": "app1",
"entityId": undefined,
"page": undefined,
"pageName": "ghf:app1",
}
`);
Object {
"applicationId": "app1",
"entityId": undefined,
"page": undefined,
"pageName": "ghf:app1",
}
`);
});
it('app name updates automatically and clears everything else', () => {
execContext.set({
type: 'ghf',
meta: {
foo: 1,
},
description: 'first set',
});
expect(execContext.get()).toStrictEqual({
name: 'app1',
description: 'first set',
type: 'ghf',
url: '/',
});
expect(execContext.get()).toMatchInlineSnapshot(
{
name: 'app1',
description: 'first set',
type: 'ghf',
url: '/',
},
`
Object {
"description": "first set",
"meta": Object {
"foo": 1,
},
"name": "app1",
"type": "ghf",
"url": "/",
}
`
);
curApp$.next('app2');
expect(execContext.get()).toStrictEqual({
name: 'app2',
url: '/',
});
expect(execContext.get()).toMatchInlineSnapshot(
{
name: 'app2',
url: '/',
},
`
Object {
"name": "app2",
"url": "/",
}
`
);
});
it('sets context and adds current url and appid when getting it', () => {
execContext.set({
type: 'ghf',
meta: {
foo: false,
},
description: 'first set',
});
expect(execContext.get()).toStrictEqual({
name: 'app1',
description: 'first set',
type: 'ghf',
url: '/',
});
expect(execContext.get()).toMatchInlineSnapshot(
{
name: 'app1',
description: 'first set',
type: 'ghf',
url: '/',
},
`
Object {
"description": "first set",
"meta": Object {
"foo": false,
},
"name": "app1",
"type": "ghf",
"url": "/",
}
`
);
});
it('merges context between calls and gets it', () => {
execContext.set({
type: 'ghf',
meta: {
foo: true,
},
description: 'first set',
});
@ -91,12 +137,25 @@ describe('ExecutionContextService', () => {
description: 'second set',
});
expect(execContext.get()).toStrictEqual({
name: 'app1',
type: 'ghf',
description: 'second set',
url: '/',
});
expect(execContext.get()).toMatchInlineSnapshot(
{
name: 'app1',
type: 'ghf',
description: 'second set',
url: '/',
},
`
Object {
"description": "second set",
"meta": Object {
"foo": true,
},
"name": "app1",
"type": "ghf",
"url": "/",
}
`
);
});
it('context observable fires the context each time it changes', () => {
@ -104,6 +163,9 @@ describe('ExecutionContextService', () => {
execContext.set({
type: 'ghf',
meta: {
foo: 'meta',
},
description: 'first set',
});
@ -113,6 +175,9 @@ describe('ExecutionContextService', () => {
name: 'app1',
type: 'ghf',
description: 'first set',
meta: {
foo: 'meta',
},
url: '/',
});
@ -124,6 +189,9 @@ describe('ExecutionContextService', () => {
expect(sub).toHaveBeenCalledWith({
name: 'app1',
type: 'str',
meta: {
foo: 'meta',
},
description: 'first set',
url: '/',
});
@ -136,6 +204,9 @@ describe('ExecutionContextService', () => {
execContext.set({
type: 'ghf',
meta: {
foo: 'test',
},
description: 'first set',
});
@ -148,6 +219,9 @@ describe('ExecutionContextService', () => {
expect(sub).toHaveBeenCalledWith({
name: 'app1',
type: 'ghf',
meta: {
foo: 'test',
},
description: 'first set',
url: '/',
});
@ -160,6 +234,9 @@ describe('ExecutionContextService', () => {
execContext.set({
type: 'ghf',
meta: {
foo: true,
},
description: 'first set',
});
execContext.context$.subscribe(sub);
@ -180,16 +257,28 @@ describe('ExecutionContextService', () => {
description: 'first set',
page: 'mypage',
child: {
meta: {
foo: 'test',
},
description: 'inner',
},
id: '123',
});
expect(execContext.getAsLabels()).toStrictEqual({
name: 'app1',
page: 'mypage',
id: '123',
});
expect(execContext.getAsLabels()).toMatchInlineSnapshot(
{
name: 'app1',
page: 'mypage',
id: '123',
},
`
Object {
"id": "123",
"name": "app1",
"page": "mypage",
}
`
);
});
it('getAsLabels removes undefined values', () => {
@ -197,22 +286,48 @@ describe('ExecutionContextService', () => {
type: 'ghf',
description: 'first set',
page: 'mypage',
meta: {
foo: false,
},
id: undefined,
});
expect(execContext.get()).toStrictEqual({
name: 'app1',
type: 'ghf',
page: 'mypage',
url: '/',
description: 'first set',
id: undefined,
});
expect(execContext.get()).toMatchInlineSnapshot(
{
name: 'app1',
type: 'ghf',
page: 'mypage',
url: '/',
description: 'first set',
id: undefined,
},
`
Object {
"description": "first set",
"id": undefined,
"meta": Object {
"foo": false,
},
"name": "app1",
"page": "mypage",
"type": "ghf",
"url": "/",
}
`
);
expect(execContext.getAsLabels()).toStrictEqual({
name: 'app1',
page: 'mypage',
});
expect(execContext.getAsLabels()).toMatchInlineSnapshot(
{
name: 'app1',
page: 'mypage',
},
`
Object {
"name": "app1",
"page": "mypage",
}
`
);
});
it('stop clears subscriptions', () => {

View file

@ -27,6 +27,8 @@ export type KibanaExecutionContext = {
readonly description?: string;
/** in browser - url to navigate to a current page, on server - endpoint path, for task: task SO url */
readonly url?: string;
/** Metadata attached to the field. An optional parameter that allows to describe the execution context in more detail. **/
readonly meta?: { [key: string]: string | number | boolean | undefined };
/** an inner context spawned from the current context. */
child?: KibanaExecutionContext;
};

View file

@ -65,6 +65,9 @@ describe('KibanaExecutionContext', () => {
type: 'test-type',
name: 'test-name',
id: '42',
meta: {
foo: true,
},
description: 'test-descripton',
};
@ -84,6 +87,9 @@ describe('KibanaExecutionContext', () => {
type: 'child-test-type',
name: 'child-test-name',
id: '42',
meta: {
foo: 'test',
},
description: 'test-descripton',
};
@ -98,6 +104,9 @@ describe('KibanaExecutionContext', () => {
id: 'Visualization☺漢字',
type: 'test☺type',
name: 'test漢name',
meta: {
foo: 'test漢name',
},
description: 'test字description',
};
@ -112,6 +121,9 @@ describe('KibanaExecutionContext', () => {
id: 'Dashboard☺漢字',
type: 'test☺type',
name: 'test漢name',
meta: {
foo: 'test漢name',
},
description: 'parent-descripton',
};
const parentContainer = new ExecutionContextContainer(parentContext);
@ -135,6 +147,9 @@ describe('KibanaExecutionContext', () => {
new ExecutionContextContainer({
type: 'test-type'.repeat(1000),
name: 'test-name',
meta: {
foo: 'test-meta',
},
id: '42'.repeat(1000),
description: 'test-descripton',
}).toString(),
@ -149,6 +164,9 @@ describe('KibanaExecutionContext', () => {
type: 'test-type',
name: 'test-name',
id: '42',
meta: {
foo: 'test',
},
description: 'test-descripton',
};
@ -161,6 +179,9 @@ describe('KibanaExecutionContext', () => {
type: 'type',
name: 'name',
id: '41',
meta: {
foo: true,
},
description: 'descripton',
};
@ -168,6 +189,9 @@ describe('KibanaExecutionContext', () => {
type: 'child-test-type',
name: 'child-test-name',
id: '42',
meta: {
foo: 42,
},
description: 'test-descripton',
};
const contextContainer = new ExecutionContextContainer(context);

View file

@ -129,6 +129,6 @@ pageLoadAssetSize:
eventAnnotation: 19334
screenshotting: 22870
synthetics: 40958
expressionXY: 34000
expressionXY: 36000
kibanaUsageCollection: 16463
kubernetesSecurity: 77234

View file

@ -23,7 +23,7 @@ export const storybookAliases = {
es_ui_shared: 'src/plugins/es_ui_shared/.storybook',
expression_error: 'src/plugins/expression_error/.storybook',
expression_image: 'src/plugins/expression_image/.storybook',
expression_metric_vis: 'src/plugins/chart_expressions/expression_metric/.storybook',
expression_metric_vis: 'src/plugins/chart_expressions/expression_legacy_metric/.storybook',
expression_metric: 'src/plugins/expression_metric/.storybook',
expression_partition_vis: 'src/plugins/chart_expressions/expression_partition_vis/.storybook',
expression_repeat_image: 'src/plugins/expression_repeat_image/.storybook',

View file

@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
export { extractContainerType, extractVisualizationType } from './utils';

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
export const extractContainerType = (context?: KibanaExecutionContext): string | undefined => {
if (context) {
const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => {
if (item.type) {
return item;
} else if (item.child) {
return recursiveGet(item.child);
}
};
return recursiveGet(context)?.type;
}
};
export const extractVisualizationType = (context?: KibanaExecutionContext): string | undefined => {
if (context) {
const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => {
if (item.child) {
return recursiveGet(item.child);
} else {
return item;
}
};
return recursiveGet(context)?.type;
}
};

View file

@ -16,6 +16,7 @@ import {
GaugeLabelMajorModes,
GaugeTicksPositions,
} from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin';
describe('interpreter/functions#gauge', () => {
const fn = functionWrapper(gaugeFunction());
@ -39,7 +40,7 @@ describe('interpreter/functions#gauge', () => {
const checkArg = (arg: keyof GaugeArguments, options: Record<string, string>) => {
Object.values(options).forEach((option) => {
it(`returns an object with the correct structure for the ${option} ${arg}`, () => {
const actual = fn(context, { ...args, [arg]: option }, undefined);
const actual = fn(context, { ...args, [arg]: option });
expect(actual).toMatchSnapshot();
});
});
@ -51,55 +52,43 @@ describe('interpreter/functions#gauge', () => {
checkArg('labelMajorMode', GaugeLabelMajorModes);
it(`returns an object with the correct structure for the circle if centralMajor and centralMajorMode are passed`, () => {
const actual = fn(
context,
{
...args,
shape: GaugeShapes.CIRCLE,
centralMajor: 'Some label',
centralMajorMode: GaugeCentralMajorModes.CUSTOM,
},
undefined
);
const actual = fn(context, {
...args,
shape: GaugeShapes.CIRCLE,
centralMajor: 'Some label',
centralMajorMode: GaugeCentralMajorModes.CUSTOM,
});
expect(actual).toMatchSnapshot();
});
it(`returns an object with the correct structure for the arc if centralMajor and centralMajorMode are passed`, () => {
const actual = fn(
context,
{
...args,
shape: GaugeShapes.ARC,
centralMajor: 'Some label',
centralMajorMode: GaugeCentralMajorModes.CUSTOM,
},
undefined
);
const actual = fn(context, {
...args,
shape: GaugeShapes.ARC,
centralMajor: 'Some label',
centralMajorMode: GaugeCentralMajorModes.CUSTOM,
});
expect(actual).toMatchSnapshot();
});
it(`throws error if centralMajor or centralMajorMode are provided for the horizontalBullet shape`, () => {
const actual = () =>
fn(
context,
{ ...args, centralMajor: 'Some label', centralMajorMode: GaugeCentralMajorModes.CUSTOM },
undefined
);
fn(context, {
...args,
centralMajor: 'Some label',
centralMajorMode: GaugeCentralMajorModes.CUSTOM,
});
expect(actual).toThrowErrorMatchingSnapshot();
});
it(`throws error if centralMajor or centralMajorMode are provided for the vertical shape`, () => {
const actual = () =>
fn(
context,
{
...args,
shape: GaugeShapes.VERTICAL_BULLET,
centralMajor: 'Some label',
centralMajorMode: GaugeCentralMajorModes.CUSTOM,
},
undefined
);
fn(context, {
...args,
shape: GaugeShapes.VERTICAL_BULLET,
centralMajor: 'Some label',
centralMajorMode: GaugeCentralMajorModes.CUSTOM,
});
expect(actual).toThrowErrorMatchingSnapshot();
});
@ -114,8 +103,10 @@ describe('interpreter/functions#gauge', () => {
reset: () => {},
},
},
};
await fn(context, args, handlers as any);
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext;
await fn(context, args, handlers);
expect(loggedTable!).toMatchSnapshot();
});

View file

@ -24,7 +24,6 @@ import {
GaugeColorModes,
GaugeCentralMajorModes,
} from '../constants';
export type GaugeColorMode = $Values<typeof GaugeColorModes>;
export type GaugeShape = $Values<typeof GaugeShapes>;
export type GaugeLabelMajorMode = $Values<typeof GaugeLabelMajorModes>;

View file

@ -11,6 +11,6 @@
"ui": true,
"requiredPlugins": ["expressions", "fieldFormats", "charts", "visualizations", "presentationUtil", "data"],
"requiredBundles": ["kibanaUtils", "kibanaReact"],
"optionalPlugins": [],
"optionalPlugins": ["usageCollection"],
"extraPublicDirs": ["common"]
}

View file

@ -9,39 +9,71 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { PersistedState } from '@kbn/visualizations-plugin/public';
import { ThemeServiceStart } from '@kbn/core/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers';
import { EXPRESSION_GAUGE_NAME, GaugeExpressionProps } from '../../common';
import { getFormatService, getPaletteService, getThemeService } from '../services';
import { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { ExpressionGaugePluginStart } from '../plugin';
import { EXPRESSION_GAUGE_NAME, GaugeExpressionProps, GaugeShapes } from '../../common';
import { getFormatService, getPaletteService } from '../services';
import { extractContainerType, extractVisualizationType } from '../../../common';
interface ExpressionGaugeRendererDependencies {
theme: ThemeServiceStart;
getStartDeps: StartServicesGetter<ExpressionGaugePluginStart>;
}
export const gaugeRenderer: (
deps: ExpressionGaugeRendererDependencies
) => ExpressionRenderDefinition<GaugeExpressionProps> = ({ theme }) => ({
) => ExpressionRenderDefinition<GaugeExpressionProps> = ({ getStartDeps }) => ({
name: EXPRESSION_GAUGE_NAME,
displayName: i18n.translate('expressionGauge.renderer.visualizationName', {
defaultMessage: 'Gauge',
}),
reuseDomNode: true,
render: async (domNode, config, handlers) => {
const { core, plugins } = getStartDeps();
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
const renderComplete = () => {
let type: string;
switch (config.args.shape) {
case GaugeShapes.HORIZONTAL_BULLET:
type = `${EXPRESSION_GAUGE_NAME}_horizontal`;
break;
case GaugeShapes.VERTICAL_BULLET:
type = `${EXPRESSION_GAUGE_NAME}_vertical`;
break;
default:
type = EXPRESSION_GAUGE_NAME;
}
const executionContext = handlers.getExecutionContext();
const containerType = extractContainerType(executionContext);
const visualizationType = extractVisualizationType(executionContext);
if (containerType && visualizationType) {
plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, [
`render_${visualizationType}_${type}`,
]);
}
handlers.done();
};
const { GaugeComponent } = await import('../components/gauge_component');
render(
<KibanaThemeProvider theme$={theme.theme$}>
<KibanaThemeProvider theme$={core.theme.theme$}>
<div className="gauge-container" data-test-subj="gaugeChart">
<GaugeComponent
{...config}
formatFactory={getFormatService().deserialize}
chartsThemeService={getThemeService()}
chartsThemeService={plugins.charts.theme}
paletteService={getPaletteService()}
renderComplete={() => handlers.done()}
renderComplete={renderComplete}
uiState={handlers.uiState as PersistedState}
/>
</div>

View file

@ -5,12 +5,14 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public';
import { CoreSetup, CoreStart } from '@kbn/core/public';
import { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { createStartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { gaugeFunction } from '../common';
import { setFormatService, setThemeService, setPaletteService } from './services';
import { setFormatService, setPaletteService } from './services';
import { gaugeRenderer } from './expression_renderers';
/** @internal */
@ -22,18 +24,25 @@ export interface ExpressionGaugePluginSetup {
/** @internal */
export interface ExpressionGaugePluginStart {
fieldFormats: FieldFormatsStart;
usageCollection?: UsageCollectionStart;
charts: ChartsPluginStart;
}
/** @internal */
export class ExpressionGaugePlugin {
public setup(core: CoreSetup, { expressions, charts }: ExpressionGaugePluginSetup) {
setThemeService(charts.theme);
public setup(
core: CoreSetup<ExpressionGaugePluginStart, void>,
{ expressions, charts }: ExpressionGaugePluginSetup
) {
charts.palettes.getPalettes().then((palettes) => {
setPaletteService(palettes);
});
const getStartDeps = createStartServicesGetter<ExpressionGaugePluginStart, void>(
core.getStartServices
);
expressions.registerFunction(gaugeFunction);
expressions.registerRenderer(gaugeRenderer({ theme: core.theme }));
expressions.registerRenderer(gaugeRenderer({ getStartDeps }));
}
public start(core: CoreStart, { fieldFormats }: ExpressionGaugePluginStart) {

View file

@ -7,5 +7,4 @@
*/
export { getFormatService, setFormatService } from './format_service';
export { getThemeService, setThemeService } from './theme_service';
export { getPaletteService, setPaletteService } from './palette_service';

View file

@ -13,8 +13,10 @@
"server/**/*",
],
"references": [
{ "path": "../tsconfig.json" },
{ "path": "../../../core/tsconfig.json" },
{ "path": "../../expressions/tsconfig.json" },
{ "path": "../../usage_collection/tsconfig.json" },
{ "path": "../../presentation_util/tsconfig.json" },
{ "path": "../../field_formats/tsconfig.json" },
{ "path": "../../charts/tsconfig.json" },

View file

@ -11,6 +11,7 @@ import type { HeatmapArguments } from '..';
import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils';
import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs';
import { EXPRESSION_HEATMAP_GRID_NAME, EXPRESSION_HEATMAP_LEGEND_NAME } from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin';
describe('interpreter/functions#heatmap', () => {
const fn = functionWrapper(heatmapFunction());
@ -56,7 +57,7 @@ describe('interpreter/functions#heatmap', () => {
};
it('returns an object with the correct structure', () => {
const actual = fn(context, args, undefined);
const actual = fn(context, args);
expect(actual).toMatchSnapshot();
});
@ -72,8 +73,10 @@ describe('interpreter/functions#heatmap', () => {
reset: () => {},
},
},
};
await fn(context, args, handlers as any);
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext;
await fn(context, args, handlers);
expect(loggedTable!).toMatchSnapshot();
});

View file

@ -11,6 +11,6 @@
"ui": true,
"requiredPlugins": ["expressions", "fieldFormats", "charts", "visualizations", "presentationUtil", "data"],
"requiredBundles": ["kibanaUtils", "kibanaReact"],
"optionalPlugins": [],
"optionalPlugins": ["usageCollection"],
"extraPublicDirs": ["common"]
}

View file

@ -9,9 +9,11 @@ import { i18n } from '@kbn/i18n';
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import type { PersistedState } from '@kbn/visualizations-plugin/public';
import { ThemeServiceStart } from '@kbn/core/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers';
import { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { ExpressionHeatmapPluginStart } from '../plugin';
import {
EXPRESSION_HEATMAP_NAME,
HeatmapExpressionProps,
@ -23,23 +25,25 @@ import {
getFormatService,
getPaletteService,
getUISettings,
getThemeService,
} from '../services';
import { getTimeZone } from '../utils/get_timezone';
import { extractContainerType, extractVisualizationType } from '../../../common';
interface ExpressioHeatmapRendererDependencies {
theme: ThemeServiceStart;
getStartDeps: StartServicesGetter<ExpressionHeatmapPluginStart>;
}
export const heatmapRenderer: (
deps: ExpressioHeatmapRendererDependencies
) => ExpressionRenderDefinition<HeatmapExpressionProps> = ({ theme }) => ({
) => ExpressionRenderDefinition<HeatmapExpressionProps> = ({ getStartDeps }) => ({
name: EXPRESSION_HEATMAP_NAME,
displayName: i18n.translate('expressionHeatmap.visualizationName', {
defaultMessage: 'Heatmap',
}),
reuseDomNode: true,
render: async (domNode, config, handlers) => {
const { core, plugins } = getStartDeps();
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
@ -50,11 +54,26 @@ export const heatmapRenderer: (
handlers.event({ name: 'brush', data });
};
const renderComplete = () => {
const executionContext = handlers.getExecutionContext();
const containerType = extractContainerType(executionContext);
const visualizationType = extractVisualizationType(executionContext);
if (containerType && visualizationType) {
plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, [
`render_${visualizationType}_${EXPRESSION_HEATMAP_NAME}`,
]);
}
handlers.done();
};
const timeZone = getTimeZone(getUISettings());
const { HeatmapComponent } = await import('../components/heatmap_component');
const { isInteractive } = handlers;
render(
<KibanaThemeProvider theme$={theme.theme$}>
<KibanaThemeProvider theme$={core.theme.theme$}>
<div className="heatmap-container" data-test-subj="heatmapChart">
<HeatmapComponent
{...config}
@ -63,9 +82,9 @@ export const heatmapRenderer: (
timeZone={timeZone}
datatableUtilities={getDatatableUtilities()}
formatFactory={getFormatService().deserialize}
chartsThemeService={getThemeService()}
chartsThemeService={plugins.charts.theme}
paletteService={getPaletteService()}
renderComplete={() => handlers.done()}
renderComplete={renderComplete}
uiState={handlers.uiState as PersistedState}
interactive={isInteractive()}
/>

View file

@ -5,18 +5,19 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public';
import { CoreSetup, CoreStart } from '@kbn/core/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { createStartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { heatmapFunction, heatmapLegendConfig, heatmapGridConfig } from '../common';
import {
setDatatableUtilities,
setFormatService,
setPaletteService,
setUISettings,
setThemeService,
} from './services';
import { heatmapRenderer } from './expression_renderers';
@ -30,20 +31,29 @@ export interface ExpressionHeatmapPluginSetup {
export interface ExpressionHeatmapPluginStart {
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
charts: ChartsPluginStart;
usageCollection?: UsageCollectionStart;
}
/** @internal */
export class ExpressionHeatmapPlugin {
public setup(core: CoreSetup, { expressions, charts }: ExpressionHeatmapPluginSetup) {
public setup(
core: CoreSetup<ExpressionHeatmapPluginStart, void>,
{ expressions, charts }: ExpressionHeatmapPluginSetup
) {
charts.palettes.getPalettes().then((palettes) => {
setPaletteService(palettes);
});
setUISettings(core.uiSettings);
setThemeService(charts.theme);
const getStartDeps = createStartServicesGetter<ExpressionHeatmapPluginStart, void>(
core.getStartServices
);
expressions.registerFunction(heatmapFunction);
expressions.registerFunction(heatmapLegendConfig);
expressions.registerFunction(heatmapGridConfig);
expressions.registerRenderer(heatmapRenderer({ theme: core.theme }));
expressions.registerRenderer(heatmapRenderer({ getStartDeps }));
}
public start(core: CoreStart, { data, fieldFormats }: ExpressionHeatmapPluginStart) {

View file

@ -8,10 +8,5 @@
export { getDatatableUtilities, setDatatableUtilities } from './datatable_utilities';
export { getFormatService, setFormatService } from './format_service';
export {
getPaletteService,
setPaletteService,
setThemeService,
getThemeService,
} from './palette_service';
export { getPaletteService, setPaletteService } from './palette_service';
export { getUISettings, setUISettings } from './ui_settings';

View file

@ -8,10 +8,6 @@
import type { PaletteRegistry } from '@kbn/coloring';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
export const [getPaletteService, setPaletteService] =
createGetterSetter<PaletteRegistry>('palette');
export const [getThemeService, setThemeService] =
createGetterSetter<ChartsPluginSetup['theme']>('charts.theme');

View file

@ -13,8 +13,10 @@
"server/**/*",
],
"references": [
{ "path": "../tsconfig.json" },
{ "path": "../../../core/tsconfig.json" },
{ "path": "../../expressions/tsconfig.json" },
{ "path": "../../usage_collection/tsconfig.json" },
{ "path": "../../presentation_util/tsconfig.json" },
{ "path": "../../field_formats/tsconfig.json" },
{ "path": "../../charts/tsconfig.json" },

View file

@ -11,6 +11,7 @@ import type { MetricArguments } from '..';
import { functionWrapper } from '@kbn/expressions-plugin/common/expression_functions/specs/tests/utils';
import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs';
import { LabelPosition } from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin';
describe('interpreter/functions#metric', () => {
const fn = functionWrapper(metricVisFunction());
@ -55,7 +56,7 @@ describe('interpreter/functions#metric', () => {
});
it('returns an object with the correct structure', () => {
const actual = fn(context, args, undefined);
const actual = fn(context, args);
expect(actual).toMatchSnapshot();
});
@ -71,8 +72,10 @@ describe('interpreter/functions#metric', () => {
reset: () => {},
},
},
};
await fn(context, args, handlers as any);
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext;
await fn(context, args, handlers);
expect(loggedTable!).toMatchSnapshot();
});
@ -88,7 +91,7 @@ describe('interpreter/functions#metric', () => {
},
};
expect(() => fn(context, args, undefined)).toThrowErrorMatchingSnapshot();
expect(() => fn(context, args)).toThrowErrorMatchingSnapshot();
});
it('returns error if several metrics and colorFullBackground specified', () => {
@ -102,13 +105,13 @@ describe('interpreter/functions#metric', () => {
},
});
expect(() => fn(context, args, undefined)).toThrowErrorMatchingSnapshot();
expect(() => fn(context, args)).toThrowErrorMatchingSnapshot();
});
it('returns error if data includes several rows and colorFullBackground specified', () => {
args.colorFullBackground = true;
context.rows.push({ 'col-0-1': 0 });
expect(() => fn(context, args, undefined)).toThrowErrorMatchingSnapshot();
expect(() => fn(context, args)).toThrowErrorMatchingSnapshot();
});
});

View file

@ -17,5 +17,5 @@
"presentationUtil"
],
"requiredBundles": ["kibanaUtils", "kibanaReact"],
"optionalPlugins": []
"optionalPlugins": ["usageCollection"]
}

View file

@ -6,8 +6,11 @@
* Side Public License, v 1.
*/
export const getFormatService = () => ({
deserialize: (target: any) => ({
convert: (text: string, format: string) => text,
}),
});
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
export const getFormatService = () =>
({
deserialize: (target: any) => ({
convert: (text: string, format: string) => text,
}),
} as FieldFormatsStart);

View file

@ -7,6 +7,7 @@
*/
import { CustomPaletteState } from '@kbn/charts-plugin/common';
import type { PaletteRegistry } from '@kbn/coloring';
export const getPaletteService = () => {
return {
@ -17,5 +18,5 @@ export const getPaletteService = () => {
return colors[lessThenValueIndex];
},
}),
};
} as unknown as PaletteRegistry;
};

View file

@ -13,9 +13,14 @@ import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
import { Datatable, DatatableColumn } from '@kbn/expressions-plugin';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { ColorMode, CustomPaletteState } from '@kbn/charts-plugin/common';
import { getFormatService } from '../__mocks__/format_service';
import { getPaletteService } from '../__mocks__/palette_service';
import { ExpressionMetricVisRendererDependencies } from '../expression_renderers/metric_vis_renderer';
import { getMetricVisRenderer } from '../expression_renderers';
import { MetricStyle, MetricVisRenderConfig, visType } from '../../common/types';
import { LabelPosition } from '../../common/constants';
import { setFormatService } from '../services/format_service';
import { setPaletteService } from '../services/palette_service';
const palette: CustomPaletteState = {
colors: ['rgb(219 231 38)', 'rgb(112 38 231)', 'rgb(38 124 231)'],
@ -124,7 +129,21 @@ const containerSize = {
height: '700px',
};
const metricVisRenderer = getMetricVisRenderer({ theme$: from([{ darkMode: false }]) });
setFormatService(getFormatService());
setPaletteService(getPaletteService());
const getStartDeps = (() => ({
core: {
theme: {
theme$: from([{ darkMode: false }]),
},
},
})) as unknown as ExpressionMetricVisRendererDependencies['getStartDeps'];
const metricVisRenderer = () =>
getMetricVisRenderer({
getStartDeps,
});
storiesOf('renderers/visMetric', module)
.add('Default', () => {

View file

@ -8,8 +8,7 @@
import React, { lazy } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { ThemeServiceStart } from '@kbn/core/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import {
ExpressionValueVisDimension,
@ -21,7 +20,10 @@ import {
} from '@kbn/expressions-plugin/common/expression_renderers';
import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils';
import { Datatable } from '@kbn/expressions-plugin';
import { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { ExpressionLegacyMetricPluginStart } from '../plugin';
import { EXPRESSION_METRIC_NAME, MetricVisRenderConfig, VisParams } from '../../common';
import { extractContainerType, extractVisualizationType } from '../../../common';
// @ts-ignore
const MetricVisComponent = lazy(() => import('../components/metric_component'));
@ -53,39 +55,59 @@ async function metricFilterable(
);
}
export const getMetricVisRenderer = (
theme: ThemeServiceStart
): (() => ExpressionRenderDefinition<MetricVisRenderConfig>) => {
return () => ({
name: EXPRESSION_METRIC_NAME,
displayName: 'metric visualization',
reuseDomNode: true,
render: async (domNode, { visData, visConfig }, handlers) => {
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
/** @internal **/
export interface ExpressionMetricVisRendererDependencies {
getStartDeps: StartServicesGetter<ExpressionLegacyMetricPluginStart>;
}
const filterable = await metricFilterable(visConfig.dimensions, visData, handlers);
export const getMetricVisRenderer: (
deps: ExpressionMetricVisRendererDependencies
) => ExpressionRenderDefinition<MetricVisRenderConfig> = ({ getStartDeps }) => ({
name: EXPRESSION_METRIC_NAME,
displayName: 'metric visualization',
reuseDomNode: true,
render: async (domNode, { visData, visConfig }, handlers) => {
const { core, plugins } = getStartDeps();
render(
<KibanaThemeProvider theme$={theme.theme$}>
<VisualizationContainer
data-test-subj="legacyMtrVis"
className="legacyMtrVis"
showNoResult={!visData.rows?.length}
handlers={handlers}
>
<MetricVisComponent
visData={visData}
visParams={visConfig}
renderComplete={() => handlers.done()}
fireEvent={handlers.event}
filterable={filterable}
/>
</VisualizationContainer>
</KibanaThemeProvider>,
domNode
);
},
});
};
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
const filterable = await metricFilterable(visConfig.dimensions, visData, handlers);
const renderComplete = () => {
const executionContext = handlers.getExecutionContext();
const containerType = extractContainerType(executionContext);
const visualizationType = extractVisualizationType(executionContext);
if (containerType && visualizationType) {
plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, [
`render_${visualizationType}_legacy_metric`,
]);
}
handlers.done();
};
render(
<KibanaThemeProvider theme$={core.theme.theme$}>
<VisualizationContainer
data-test-subj="legacyMtrVis"
className="legacyMtrVis"
showNoResult={!visData.rows?.length}
renderComplete={renderComplete}
handlers={handlers}
>
<MetricVisComponent
visData={visData}
visParams={visConfig}
renderComplete={renderComplete}
fireEvent={handlers.event}
filterable={filterable}
/>
</VisualizationContainer>
</KibanaThemeProvider>,
domNode
);
},
});

View file

@ -10,6 +10,8 @@ import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { createStartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { metricVisFunction } from '../common';
import { setFormatService, setPaletteService } from './services';
import { getMetricVisRenderer } from './expression_renderers';
@ -23,16 +25,25 @@ export interface ExpressionLegacyMetricPluginSetup {
/** @internal */
export interface ExpressionLegacyMetricPluginStart {
fieldFormats: FieldFormatsStart;
usageCollection?: UsageCollectionStart;
}
/** @internal */
export class ExpressionLegacyMetricPlugin implements Plugin<void, void> {
public setup(core: CoreSetup, { expressions, charts }: ExpressionLegacyMetricPluginSetup) {
expressions.registerFunction(metricVisFunction);
expressions.registerRenderer(getMetricVisRenderer(core.theme));
export class ExpressionLegacyMetricPlugin implements Plugin {
public setup(
core: CoreSetup<ExpressionLegacyMetricPluginStart, void>,
{ expressions, charts }: ExpressionLegacyMetricPluginSetup
) {
const getStartDeps = createStartServicesGetter<ExpressionLegacyMetricPluginStart, void>(
core.getStartServices
);
charts.palettes.getPalettes().then((palettes) => {
setPaletteService(palettes);
});
expressions.registerFunction(metricVisFunction);
expressions.registerRenderer(getMetricVisRenderer({ getStartDeps }));
}
public start(core: CoreStart, { fieldFormats }: ExpressionLegacyMetricPluginStart) {

View file

@ -13,11 +13,13 @@
"server/**/*",
],
"references": [
{ "path": "../tsconfig.json" },
{ "path": "../../../core/tsconfig.json" },
{ "path": "../../expressions/tsconfig.json" },
{ "path": "../../presentation_util/tsconfig.json" },
{ "path": "../../field_formats/tsconfig.json" },
{ "path": "../../charts/tsconfig.json" },
{ "path": "../../usage_collection/tsconfig.json" },
{ "path": "../../visualizations/tsconfig.json" },
]
}

View file

@ -17,5 +17,5 @@
"presentationUtil"
],
"requiredBundles": ["kibanaUtils", "kibanaReact"],
"optionalPlugins": []
"optionalPlugins": ["usageCollection"]
}

View file

@ -9,29 +9,52 @@
import React, { lazy } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { ThemeServiceStart } from '@kbn/core/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers';
import { VisualizationContainer } from '@kbn/visualizations-plugin/public';
import { css } from '@emotion/react';
import { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { ExpressionMetricPluginStart } from '../plugin';
import { EXPRESSION_METRIC_NAME, MetricVisRenderConfig } from '../../common';
import { extractContainerType, extractVisualizationType } from '../../../common';
const MetricVis = lazy(() => import('../components/metric_vis'));
interface ExpressionMetricVisRendererDependencies {
getStartDeps: StartServicesGetter<ExpressionMetricPluginStart>;
}
export const getMetricVisRenderer = (
theme: ThemeServiceStart
deps: ExpressionMetricVisRendererDependencies
): (() => ExpressionRenderDefinition<MetricVisRenderConfig>) => {
return () => ({
name: EXPRESSION_METRIC_NAME,
displayName: 'metric visualization',
reuseDomNode: true,
render: async (domNode, { visData, visConfig }, handlers) => {
const { core, plugins } = deps.getStartDeps();
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
const renderComplete = () => {
const executionContext = handlers.getExecutionContext();
const containerType = extractContainerType(executionContext);
const visualizationType = extractVisualizationType(executionContext);
if (containerType && visualizationType) {
plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, [
`render_${visualizationType}_metric`,
]);
}
handlers.done();
};
render(
<KibanaThemeProvider theme$={theme.theme$}>
<KibanaThemeProvider theme$={core.theme.theme$}>
<VisualizationContainer
data-test-subj="mtrVis"
css={css`
@ -39,9 +62,10 @@ export const getMetricVisRenderer = (
width: 100%;
`}
showNoResult={!visData.rows.length}
renderComplete={renderComplete}
handlers={handlers}
>
<MetricVis data={visData} config={visConfig} renderComplete={() => handlers.done()} />
<MetricVis data={visData} config={visConfig} renderComplete={renderComplete} />
</VisualizationContainer>
</KibanaThemeProvider>,
domNode

View file

@ -10,6 +10,8 @@ import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { createStartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { metricVisFunction } from '../common';
import { setFormatService, setPaletteService } from './services';
import { getMetricVisRenderer } from './expression_renderers';
@ -25,18 +27,28 @@ export interface ExpressionMetricPluginSetup {
/** @internal */
export interface ExpressionMetricPluginStart {
fieldFormats: FieldFormatsStart;
usageCollection?: UsageCollectionStart;
}
/** @internal */
export class ExpressionMetricPlugin implements Plugin<void, void> {
public async setup(core: CoreSetup, { expressions, charts }: ExpressionMetricPluginSetup) {
export class ExpressionMetricPlugin implements Plugin {
public setup(
core: CoreSetup<ExpressionMetricPluginStart, void>,
{ expressions, charts }: ExpressionMetricPluginSetup
) {
const getStartDeps = createStartServicesGetter<ExpressionMetricPluginStart, void>(
core.getStartServices
);
charts.palettes.getPalettes().then((palettes) => {
setPaletteService(palettes);
});
expressions.registerFunction(metricVisFunction);
expressions.registerRenderer(getMetricVisRenderer(core.theme));
expressions.registerRenderer(getMetricVisRenderer({ getStartDeps }));
setUiSettingsService(core.uiSettings);
setThemeService(charts.theme);
const palettes = await charts.palettes.getPalettes();
setPaletteService(palettes);
}
public start(core: CoreStart, { fieldFormats }: ExpressionMetricPluginStart) {

View file

@ -13,8 +13,10 @@
"server/**/*",
],
"references": [
{ "path": "../tsconfig.json" },
{ "path": "../../../core/tsconfig.json" },
{ "path": "../../expressions/tsconfig.json" },
{ "path": "../../usage_collection/tsconfig.json" },
{ "path": "../../presentation_util/tsconfig.json" },
{ "path": "../../field_formats/tsconfig.json" },
{ "path": "../../charts/tsconfig.json" },

View file

@ -17,6 +17,7 @@ import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs';
import { mosaicVisFunction } from './mosaic_vis_function';
import { PARTITION_LABELS_VALUE } from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin';
describe('interpreter/functions#mosaicVis', () => {
const fn = functionWrapper(mosaicVisFunction());
@ -138,8 +139,10 @@ describe('interpreter/functions#mosaicVis', () => {
reset: () => {},
},
},
};
await fn(context, visConfig, handlers as any);
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext;
await fn(context, visConfig, handlers);
expect(loggedTable!).toMatchSnapshot();
});

View file

@ -18,6 +18,7 @@ import { ExpressionValueVisDimension, LegendSize } from '@kbn/visualizations-plu
import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs';
import { pieVisFunction } from './pie_vis_function';
import { PARTITION_LABELS_VALUE } from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin';
describe('interpreter/functions#pieVis', () => {
const fn = functionWrapper(pieVisFunction());
@ -132,7 +133,9 @@ describe('interpreter/functions#pieVis', () => {
reset: () => {},
},
},
};
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext;
await fn(context, visConfig, handlers as any);
expect(loggedTable!).toMatchSnapshot();

View file

@ -17,6 +17,7 @@ import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs';
import { treemapVisFunction } from './treemap_vis_function';
import { PARTITION_LABELS_VALUE } from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin';
describe('interpreter/functions#treemapVis', () => {
const fn = functionWrapper(treemapVisFunction());
@ -138,8 +139,10 @@ describe('interpreter/functions#treemapVis', () => {
reset: () => {},
},
},
};
await fn(context, visConfig, handlers as any);
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext;
await fn(context, visConfig, handlers);
expect(loggedTable!).toMatchSnapshot();
});

View file

@ -17,6 +17,7 @@ import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs';
import { waffleVisFunction } from './waffle_vis_function';
import { PARTITION_LABELS_VALUE } from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin';
describe('interpreter/functions#waffleVis', () => {
const fn = functionWrapper(waffleVisFunction());
@ -109,8 +110,10 @@ describe('interpreter/functions#waffleVis', () => {
reset: () => {},
},
},
};
await fn(context, visConfig, handlers as any);
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext;
await fn(context, visConfig, handlers);
expect(loggedTable!).toMatchSnapshot();
});

View file

@ -13,6 +13,6 @@
"common"
],
"requiredPlugins": ["charts", "data", "expressions", "visualizations", "fieldFormats", "presentationUtil"],
"requiredBundles": ["kibanaReact"],
"optionalPlugins": []
"requiredBundles": ["kibanaUtils", "kibanaReact"],
"optionalPlugins": ["usageCollection"]
}

View file

@ -11,10 +11,9 @@ import { action } from '@storybook/addon-actions';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { getFormatService } from './format_service';
const theme = {
theme$: from([{ darkMode: false }]),
};
import { palettes } from './palettes';
import { theme } from './theme';
import { VisTypePieDependencies } from '../plugin';
const data = {
actions: {
@ -23,8 +22,20 @@ const data = {
},
} as DataPublicPluginStart;
export const getStartDeps = async () => ({
export const getStartDeps = (() => ({
data,
fieldFormats: getFormatService() as FieldFormatsStart,
kibanaTheme: theme,
});
core: {
theme: {
theme$: from([{ darkMode: false }]),
},
},
plugins: {
data,
fieldFormats: getFormatService() as FieldFormatsStart,
charts: {
theme,
palettes,
},
},
})) as unknown as VisTypePieDependencies['getStartDeps'];

View file

@ -11,7 +11,7 @@ import { ComponentStory } from '@storybook/react';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { getPartitionVisRenderer } from '../expression_renderers';
import { ChartTypes, RenderValue } from '../../common/types';
import { palettes, theme, getStartDeps } from '../__mocks__';
import { getStartDeps } from '../__mocks__';
import { mosaicArgTypes, treemapMosaicConfig, data } from './shared';
const containerSize = {
@ -19,7 +19,7 @@ const containerSize = {
height: '700px',
};
const PartitionVisRenderer = () => getPartitionVisRenderer({ palettes, theme, getStartDeps });
const PartitionVisRenderer = () => getPartitionVisRenderer({ getStartDeps });
type Props = {
visType: RenderValue['visType'];

View file

@ -11,7 +11,7 @@ import { ComponentStory } from '@storybook/react';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { getPartitionVisRenderer } from '../expression_renderers';
import { ChartTypes, RenderValue } from '../../common/types';
import { palettes, theme, getStartDeps } from '../__mocks__';
import { getStartDeps } from '../__mocks__';
import { pieDonutArgTypes, pieConfig, data } from './shared';
const containerSize = {
@ -19,7 +19,7 @@ const containerSize = {
height: '700px',
};
const PartitionVisRenderer = () => getPartitionVisRenderer({ palettes, theme, getStartDeps });
const PartitionVisRenderer = () => getPartitionVisRenderer({ getStartDeps });
type Props = {
visType: RenderValue['visType'];

View file

@ -11,7 +11,7 @@ import { ComponentStory } from '@storybook/react';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { getPartitionVisRenderer } from '../expression_renderers';
import { ChartTypes, RenderValue } from '../../common/types';
import { palettes, theme, getStartDeps } from '../__mocks__';
import { getStartDeps } from '../__mocks__';
import { treemapArgTypes, treemapMosaicConfig, data } from './shared';
const containerSize = {
@ -19,7 +19,7 @@ const containerSize = {
height: '700px',
};
const PartitionVisRenderer = () => getPartitionVisRenderer({ palettes, theme, getStartDeps });
const PartitionVisRenderer = () => getPartitionVisRenderer({ getStartDeps });
type Props = {
visType: RenderValue['visType'];

View file

@ -11,7 +11,7 @@ import { ComponentStory } from '@storybook/react';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { getPartitionVisRenderer } from '../expression_renderers';
import { ChartTypes, RenderValue } from '../../common/types';
import { palettes, theme, getStartDeps } from '../__mocks__';
import { getStartDeps } from '../__mocks__';
import { waffleArgTypes, waffleConfig, data } from './shared';
const containerSize = {
@ -19,7 +19,7 @@ const containerSize = {
height: '700px',
};
const PartitionVisRenderer = () => getPartitionVisRenderer({ palettes, theme, getStartDeps });
const PartitionVisRenderer = () => getPartitionVisRenderer({ getStartDeps });
type Props = {
visType: RenderValue['visType'];

View file

@ -60,7 +60,6 @@ import {
} from '../utils';
import { ChartSplit, SMALL_MULTIPLES_ID } from './chart_split';
import { VisualizationNoResults } from './visualization_noresults';
import { VisTypePiePluginStartDependencies } from '../plugin';
import {
partitionVisWrapperStyle,
partitionVisContainerStyle,
@ -68,7 +67,7 @@ import {
} from './partition_vis_component.styles';
import { ChartTypes } from '../../common/types';
import { filterOutConfig } from '../utils/filter_out_config';
import { FilterEvent } from '../types';
import { FilterEvent, StartDeps } from '../types';
declare global {
interface Window {
@ -87,14 +86,13 @@ export interface PartitionVisComponentProps {
renderComplete: IInterpreterRenderHandlers['done'];
chartsThemeService: ChartsPluginSetup['theme'];
palettesRegistry: PaletteRegistry;
services: VisTypePiePluginStartDependencies;
services: Pick<StartDeps, 'data' | 'fieldFormats'>;
syncColors: boolean;
}
const PartitionVisComponent = (props: PartitionVisComponentProps) => {
const { visData, visParams: preVisParams, visType, services, syncColors } = props;
const visParams = useMemo(() => filterOutConfig(visType, preVisParams), [preVisParams, visType]);
const chartTheme = props.chartsThemeService.useChartsTheme();
const chartBaseTheme = props.chartsThemeService.useChartsBaseTheme();

View file

@ -15,9 +15,11 @@ import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/public';
import type { PersistedState } from '@kbn/visualizations-plugin/public';
import { withSuspense } from '@kbn/presentation-util-plugin/public';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { VisTypePieDependencies } from '../plugin';
import { PARTITION_VIS_RENDERER_NAME } from '../../common/constants';
import { ChartTypes, RenderValue } from '../../common/types';
import { extractContainerType, extractVisualizationType } from '../../../common';
export const strings = {
getDisplayName: () =>
@ -41,33 +43,47 @@ const partitionVisRenderer = css({
export const getPartitionVisRenderer: (
deps: VisTypePieDependencies
) => ExpressionRenderDefinition<RenderValue> = ({ theme, palettes, getStartDeps }) => ({
) => ExpressionRenderDefinition<RenderValue> = ({ getStartDeps }) => ({
name: PARTITION_VIS_RENDERER_NAME,
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render: async (domNode, { visConfig, visData, visType, syncColors }, handlers) => {
const { core, plugins } = getStartDeps();
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
const services = await getStartDeps();
const palettesRegistry = await palettes.getPalettes();
const renderComplete = () => {
const executionContext = handlers.getExecutionContext();
const containerType = extractContainerType(executionContext);
const visualizationType = extractVisualizationType(executionContext);
if (containerType && visualizationType) {
plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, [
`render_${visualizationType}_${visType}`,
]);
}
handlers.done();
};
const palettesRegistry = await plugins.charts.palettes.getPalettes();
render(
<I18nProvider>
<KibanaThemeProvider theme$={services.kibanaTheme.theme$}>
<KibanaThemeProvider theme$={core.theme.theme$}>
<div css={partitionVisRenderer}>
<PartitionVisComponent
chartsThemeService={theme}
chartsThemeService={plugins.charts.theme}
palettesRegistry={palettesRegistry}
visParams={visConfig}
visData={visData}
visType={visConfig.isDonut ? ChartTypes.DONUT : visType}
renderComplete={() => handlers.done()}
renderComplete={renderComplete}
fireEvent={handlers.event}
uiState={handlers.uiState as PersistedState}
services={{ data: services.data, fieldFormats: services.fieldFormats }}
services={{ data: plugins.data, fieldFormats: plugins.fieldFormats }}
syncColors={syncColors}
/>
</div>

View file

@ -6,10 +6,8 @@
* Side Public License, v 1.
*/
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { CoreSetup, CoreStart, ThemeServiceStart } from '@kbn/core/public';
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { CoreSetup, CoreStart } from '@kbn/core/public';
import { createStartServicesGetter, StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import {
partitionLabelsFunction,
pieVisFunction,
@ -27,23 +25,12 @@ import {
/** @internal */
export interface VisTypePieDependencies {
theme: ChartsPluginSetup['theme'];
palettes: ChartsPluginSetup['palettes'];
getStartDeps: () => Promise<{
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
kibanaTheme: ThemeServiceStart;
}>;
}
export interface VisTypePiePluginStartDependencies {
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
getStartDeps: StartServicesGetter<StartDeps>;
}
export class ExpressionPartitionVisPlugin {
public setup(
core: CoreSetup<VisTypePiePluginStartDependencies>,
core: CoreSetup<StartDeps, void>,
{ expressions, charts }: SetupDeps
): ExpressionPartitionVisPluginSetup {
expressions.registerFunction(partitionLabelsFunction);
@ -52,16 +39,9 @@ export class ExpressionPartitionVisPlugin {
expressions.registerFunction(mosaicVisFunction);
expressions.registerFunction(waffleVisFunction);
const getStartDeps = async () => {
const [coreStart, deps] = await core.getStartServices();
const { data, fieldFormats } = deps;
const { theme: kibanaTheme } = coreStart;
return { data, fieldFormats, kibanaTheme };
};
const getStartDeps = createStartServicesGetter<StartDeps, void>(core.getStartServices);
expressions.registerRenderer(
getPartitionVisRenderer({ theme: charts.theme, palettes: charts.palettes, getStartDeps })
);
expressions.registerRenderer(getPartitionVisRenderer({ getStartDeps }));
}
public start(core: CoreStart, deps: StartDeps): ExpressionPartitionVisPluginStart {}

View file

@ -6,11 +6,14 @@
* Side Public License, v 1.
*/
import type { ValueClickContext } from '@kbn/embeddable-plugin/public';
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public';
import {
Plugin as ExpressionsPublicPlugin,
ExpressionsServiceStart,
} from '@kbn/expressions-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
export type ExpressionPartitionVisPluginSetup = void;
export type ExpressionPartitionVisPluginStart = void;
@ -22,6 +25,10 @@ export interface SetupDeps {
export interface StartDeps {
expression: ExpressionsServiceStart;
charts: ChartsPluginStart;
data: DataPublicPluginStart;
fieldFormats: FieldFormatsStart;
usageCollection?: UsageCollectionStart;
}
export interface FilterEvent {

View file

@ -13,8 +13,10 @@
"server/**/*",
],
"references": [
{ "path": "../tsconfig.json" },
{ "path": "../../../core/tsconfig.json" },
{ "path": "../../expressions/tsconfig.json" },
{ "path": "../../usage_collection/tsconfig.json" },
{ "path": "../../presentation_util/tsconfig.json" },
{ "path": "../../data/tsconfig.json" },
{ "path": "../../field_formats/tsconfig.json" },

View file

@ -12,6 +12,7 @@ import { functionWrapper } from '@kbn/expressions-plugin/common/expression_funct
import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/public';
import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs';
import { ScaleOptions, Orientation } from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin';
type Arguments = Parameters<ReturnType<typeof tagcloudFunction>['fn']>[1];
@ -76,12 +77,12 @@ describe('interpreter/functions#tagcloud', () => {
};
it('returns an object with the correct structure for number accessors', () => {
const actual = fn(context, { ...visConfig, ...numberAccessors } as Arguments, undefined);
const actual = fn(context, { ...visConfig, ...numberAccessors } as Arguments);
expect(actual).toMatchSnapshot();
});
it('returns an object with the correct structure for string accessors', () => {
const actual = fn(context, { ...visConfig, ...stringAccessors } as Arguments, undefined);
const actual = fn(context, { ...visConfig, ...stringAccessors } as Arguments);
expect(actual).toMatchSnapshot();
});
@ -96,8 +97,10 @@ describe('interpreter/functions#tagcloud', () => {
reset: () => {},
},
},
};
await fn(context, { ...visConfig, ...numberAccessors } as Arguments, handlers as any);
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext;
await fn(context, { ...visConfig, ...numberAccessors } as Arguments, handlers);
expect(loggedTable!).toMatchSnapshot();
});

View file

@ -6,7 +6,7 @@
"ui": true,
"requiredPlugins": ["expressions", "visualizations", "charts", "presentationUtil", "fieldFormats"],
"requiredBundles": ["kibanaUtils", "kibanaReact"],
"optionalPlugins": [],
"optionalPlugins": ["usageCollection"],
"owner": {
"name": "Vis Editors",
"githubTeam": "kibana-vis-editors"

View file

@ -9,11 +9,12 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { Render } from '@kbn/presentation-util-plugin/public/__stories__';
import { from } from 'rxjs';
import { tagcloudRenderer } from '../expression_renderers';
import { TagcloudRendererConfig } from '../../common/types';
import { ScaleOptions, Orientation } from '../../common/constants';
import { palettes } from '../__mocks__/palettes';
import { theme } from '../__mocks__/theme';
import { ExpressionTagcloudRendererDependencies } from '../plugin';
const config: TagcloudRendererConfig = {
visType: 'tagcloud',
@ -65,11 +66,24 @@ const containerSize = {
height: '700px',
};
const getStartDeps = (() => ({
core: {
theme: {
theme$: from([{ darkMode: false }]),
},
},
plugins: {
charts: {
palettes,
},
},
})) as ExpressionTagcloudRendererDependencies['getStartDeps'];
storiesOf('renderers/tag_cloud_vis', module)
.add('Default', () => {
return (
<Render
renderer={() => tagcloudRenderer({ palettes, theme })}
renderer={() => tagcloudRenderer({ getStartDeps })}
config={config}
{...containerSize}
/>
@ -78,7 +92,7 @@ storiesOf('renderers/tag_cloud_vis', module)
.add('With log scale', () => {
return (
<Render
renderer={() => tagcloudRenderer({ palettes, theme })}
renderer={() => tagcloudRenderer({ getStartDeps })}
config={{ ...config, visParams: { ...config.visParams, scale: ScaleOptions.LOG } }}
{...containerSize}
/>
@ -87,7 +101,7 @@ storiesOf('renderers/tag_cloud_vis', module)
.add('With square root scale', () => {
return (
<Render
renderer={() => tagcloudRenderer({ palettes, theme })}
renderer={() => tagcloudRenderer({ getStartDeps })}
config={{ ...config, visParams: { ...config.visParams, scale: ScaleOptions.SQUARE_ROOT } }}
{...containerSize}
/>
@ -96,7 +110,7 @@ storiesOf('renderers/tag_cloud_vis', module)
.add('With right angled orientation', () => {
return (
<Render
renderer={() => tagcloudRenderer({ palettes, theme })}
renderer={() => tagcloudRenderer({ getStartDeps })}
config={{
...config,
visParams: { ...config.visParams, orientation: Orientation.RIGHT_ANGLED },
@ -108,7 +122,7 @@ storiesOf('renderers/tag_cloud_vis', module)
.add('With multiple orientations', () => {
return (
<Render
renderer={() => tagcloudRenderer({ palettes, theme })}
renderer={() => tagcloudRenderer({ getStartDeps })}
config={{
...config,
visParams: { ...config.visParams, orientation: Orientation.MULTIPLE },
@ -120,7 +134,7 @@ storiesOf('renderers/tag_cloud_vis', module)
.add('With hidden label', () => {
return (
<Render
renderer={() => tagcloudRenderer({ palettes, theme })}
renderer={() => tagcloudRenderer({ getStartDeps })}
config={{ ...config, visParams: { ...config.visParams, showLabel: false } }}
{...containerSize}
/>
@ -129,7 +143,7 @@ storiesOf('renderers/tag_cloud_vis', module)
.add('With empty results', () => {
return (
<Render
renderer={() => tagcloudRenderer({ palettes, theme })}
renderer={() => tagcloudRenderer({ getStartDeps })}
config={{ ...config, visData: { ...config.visData, rows: [] } }}
{...containerSize}
/>

View file

@ -14,9 +14,11 @@ import { i18n } from '@kbn/i18n';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { VisualizationContainer } from '@kbn/visualizations-plugin/public';
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers';
import { ExpressioTagcloudRendererDependencies } from '../plugin';
import { METRIC_TYPE } from '@kbn/analytics';
import { ExpressionTagcloudRendererDependencies } from '../plugin';
import { TagcloudRendererConfig } from '../../common/types';
import { EXPRESSION_NAME } from '../../common';
import { extractContainerType, extractVisualizationType } from '../../../common';
export const strings = {
getDisplayName: () =>
@ -36,22 +38,39 @@ const tagCloudVisClass = {
const TagCloudChart = lazy(() => import('../components/tagcloud_component'));
export const tagcloudRenderer: (
deps: ExpressioTagcloudRendererDependencies
) => ExpressionRenderDefinition<TagcloudRendererConfig> = ({ palettes, theme }) => ({
deps: ExpressionTagcloudRendererDependencies
) => ExpressionRenderDefinition<TagcloudRendererConfig> = ({ getStartDeps }) => ({
name: EXPRESSION_NAME,
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render: async (domNode, config, handlers) => {
const { core, plugins } = getStartDeps();
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
const palettesRegistry = await palettes.getPalettes();
const renderComplete = () => {
const executionContext = handlers.getExecutionContext();
const containerType = extractContainerType(executionContext);
const visualizationType = extractVisualizationType(executionContext);
if (containerType && visualizationType) {
plugins.usageCollection?.reportUiCounter(containerType, METRIC_TYPE.COUNT, [
`render_${visualizationType}_${EXPRESSION_NAME}`,
]);
}
handlers.done();
};
const palettesRegistry = await plugins.charts.palettes.getPalettes();
const showNoResult = config.visData.rows.length === 0;
render(
<KibanaThemeProvider theme$={theme.theme$}>
<KibanaThemeProvider theme$={core.theme.theme$}>
<I18nProvider>
<ClassNames>
{({ css, cx }) => (
@ -60,12 +79,13 @@ export const tagcloudRenderer: (
// Class `tagCloudContainer` is generated by `@emotion/react` and passed as a defined class to `VisualizationContainer`.
// It is used for rendering at `Canvas`.
className={cx('tagCloudContainer', css(tagCloudVisClass))}
renderComplete={renderComplete}
showNoResult={showNoResult}
>
<TagCloudChart
{...config}
palettesRegistry={palettesRegistry}
renderComplete={() => handlers.done()}
renderComplete={renderComplete}
fireEvent={handlers.event}
syncColors={config.syncColors}
/>

View file

@ -6,10 +6,12 @@
* Side Public License, v 1.
*/
import { CoreSetup, CoreStart, Plugin, ThemeServiceStart } from '@kbn/core/public';
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { ExpressionsStart, ExpressionsSetup } from '@kbn/expressions-plugin/public';
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { createStartServicesGetter, StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import { tagcloudRenderer } from './expression_renderers';
import { tagcloudFunction } from '../common/expression_functions';
import { setFormatService } from './format_service';
@ -20,14 +22,15 @@ interface SetupDeps {
}
/** @internal */
export interface ExpressioTagcloudRendererDependencies {
palettes: ChartsPluginSetup['palettes'];
theme: ThemeServiceStart;
export interface ExpressionTagcloudRendererDependencies {
getStartDeps: StartServicesGetter<StartDeps>;
}
interface StartDeps {
expression: ExpressionsStart;
charts: ChartsPluginStart;
fieldFormats: FieldFormatsStart;
usageCollection?: UsageCollectionStart;
}
export type ExpressionTagcloudPluginSetup = void;
@ -37,13 +40,16 @@ export class ExpressionTagcloudPlugin
implements
Plugin<ExpressionTagcloudPluginSetup, ExpressionTagcloudPluginStart, SetupDeps, StartDeps>
{
public setup(core: CoreSetup, { expressions, charts }: SetupDeps): ExpressionTagcloudPluginSetup {
const rendererDependencies: ExpressioTagcloudRendererDependencies = {
palettes: charts.palettes,
theme: core.theme,
};
public setup(
core: CoreSetup<StartDeps, ExpressionTagcloudPluginStart>,
{ expressions, charts }: SetupDeps
): ExpressionTagcloudPluginSetup {
const getStartDeps = createStartServicesGetter<StartDeps, ExpressionTagcloudPluginStart>(
core.getStartServices
);
expressions.registerFunction(tagcloudFunction);
expressions.registerRenderer(tagcloudRenderer(rendererDependencies));
expressions.registerRenderer(tagcloudRenderer({ getStartDeps }));
}
public start(core: CoreStart, { fieldFormats }: StartDeps): ExpressionTagcloudPluginStart {

View file

@ -13,9 +13,11 @@
"server/**/*",
],
"references": [
{ "path": "../tsconfig.json" },
{ "path": "../../../core/tsconfig.json" },
{ "path": "../../presentation_util/tsconfig.json" },
{ "path": "../../expressions/tsconfig.json" },
{ "path": "../../usage_collection/tsconfig.json" },
{ "path": "../../visualizations/tsconfig.json" },
{ "path": "../../charts/tsconfig.json" },
{ "path": "../../field_formats/tsconfig.json" },

View file

@ -6,8 +6,8 @@
* Side Public License, v 1.
*/
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import { LayerTypes } from '../constants';
import { DataLayerConfig, CommonXYLayerConfig } from '..';
export const [getThemeService, setThemeService] =
createGetterSetter<ChartsPluginSetup['theme']>('charts.theme');
export const isDataLayer = (layer: CommonXYLayerConfig): layer is DataLayerConfig =>
layer.layerType === LayerTypes.DATA;

View file

@ -11,5 +11,5 @@
"ui": true,
"requiredPlugins": ["expressions", "charts", "data", "fieldFormats", "uiActions", "eventAnnotation", "visualizations"],
"requiredBundles": ["kibanaReact"],
"optionalPlugins": []
"optionalPlugins": ["usageCollection"]
}

View file

@ -11,6 +11,7 @@ import { I18nProvider } from '@kbn/i18n-react';
import { ThemeServiceStart } from '@kbn/core/public';
import React from 'react';
import ReactDOM from 'react-dom';
import { METRIC_TYPE } from '@kbn/analytics';
import type { PaletteRegistry } from '@kbn/coloring';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
@ -18,8 +19,12 @@ import { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public'
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin';
import { FormatFactory } from '@kbn/field-formats-plugin/common';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import type { XYChartProps } from '../../common';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { isDataLayer } from '../../common/utils/layer_types_guards';
import { LayerTypes, SeriesTypes } from '../../common/constants';
import type { CommonXYLayerConfig, XYChartProps } from '../../common';
import type { BrushEvent, FilterEvent } from '../types';
import { extractContainerType, extractVisualizationType } from '../../../common';
export type GetStartDepsFn = () => Promise<{
data: DataPublicPluginStart;
@ -31,12 +36,60 @@ export type GetStartDepsFn = () => Promise<{
useLegacyTimeAxis: boolean;
kibanaTheme: ThemeServiceStart;
eventAnnotationService: EventAnnotationServiceType;
usageCollection?: UsageCollectionStart;
}>;
interface XyChartRendererDeps {
getStartDeps: GetStartDepsFn;
}
const extractCounterEvents = (originatingApp: string, layers: CommonXYLayerConfig[]) => {
const dataLayer = layers.find(isDataLayer);
if (dataLayer) {
const type =
dataLayer.seriesType === SeriesTypes.BAR
? `${dataLayer.isHorizontal ? 'horizontal_bar' : 'vertical_bar'}`
: dataLayer.seriesType;
const byTypes = layers.reduce(
(acc, item) => {
if (
!acc.mixedXY &&
item.layerType === LayerTypes.DATA &&
item.seriesType !== dataLayer.seriesType
) {
acc.mixedXY = true;
}
acc[item.layerType] += 1;
return acc;
},
{
mixedXY: false,
[LayerTypes.REFERENCELINE]: 0,
[LayerTypes.ANNOTATIONS]: 0,
[LayerTypes.DATA]: 0,
}
);
return [
[
type,
dataLayer.isPercentage ? 'percentage' : undefined,
dataLayer.isStacked ? 'stacked' : undefined,
]
.filter(Boolean)
.join('_'),
byTypes[LayerTypes.REFERENCELINE] ? 'reference_layer' : undefined,
byTypes[LayerTypes.ANNOTATIONS] ? 'annotation_layer' : undefined,
byTypes[LayerTypes.DATA] > 1 ? 'multiple_data_layers' : undefined,
byTypes.mixedXY ? 'mixed_xy' : undefined,
]
.filter(Boolean)
.map((item) => `render_${originatingApp}_${item}`);
}
};
export const getXyChartRenderer = ({
getStartDeps,
}: XyChartRendererDeps): ExpressionRenderDefinition<XYChartProps> => ({
@ -48,6 +101,8 @@ export const getXyChartRenderer = ({
validate: () => undefined,
reuseDomNode: true,
render: async (domNode: Element, config: XYChartProps, handlers) => {
const deps = await getStartDeps();
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
const onClickValue = (data: FilterEvent['data']) => {
handlers.event({ name: 'filter', data });
@ -55,7 +110,22 @@ export const getXyChartRenderer = ({
const onSelectRange = (data: BrushEvent['data']) => {
handlers.event({ name: 'brush', data });
};
const deps = await getStartDeps();
const renderComplete = () => {
const executionContext = handlers.getExecutionContext();
const containerType = extractContainerType(executionContext);
const visualizationType = extractVisualizationType(executionContext);
if (deps.usageCollection && containerType && visualizationType) {
const uiEvents = extractCounterEvents(visualizationType, config.args.layers);
if (uiEvents) {
deps.usageCollection.reportUiCounter(containerType, METRIC_TYPE.COUNT, uiEvents);
}
}
handlers.done();
};
const [{ XYChartReportable }, { calculateMinInterval }] = await Promise.all([
import('../components/xy_chart'),
@ -86,7 +156,7 @@ export const getXyChartRenderer = ({
renderMode={handlers.getRenderMode()}
syncColors={handlers.isSyncColorsEnabled()}
syncTooltips={handlers.isSyncTooltipsEnabled()}
renderComplete={() => handlers.done()}
renderComplete={renderComplete}
/>
</div>{' '}
</I18nProvider>

View file

@ -13,6 +13,7 @@ import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { ChartsPluginStart } from '@kbn/charts-plugin/public';
import { CoreSetup, CoreStart, IUiSettingsClient } from '@kbn/core/public';
import { EventAnnotationPluginSetup } from '@kbn/event-annotation-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { ExpressionXyPluginSetup, ExpressionXyPluginStart, SetupDeps } from './types';
import {
xyVisFunction,
@ -36,6 +37,7 @@ export interface XYPluginStartDependencies {
fieldFormats: FieldFormatsStart;
charts: ChartsPluginStart;
eventAnnotation: EventAnnotationPluginSetup;
usageCollection?: UsageCollectionStart;
}
export function getTimeZone(uiSettings: IUiSettingsClient) {
@ -70,6 +72,7 @@ export class ExpressionXyPlugin {
const [coreStart, deps] = await core.getStartServices();
const {
data,
usageCollection,
fieldFormats,
eventAnnotation,
charts: { activeCursor, theme, palettes },
@ -86,6 +89,7 @@ export class ExpressionXyPlugin {
formatFactory: fieldFormats.deserialize,
kibanaTheme,
theme,
usageCollection,
activeCursor,
paletteService,
useLegacyTimeAxis,

View file

@ -13,10 +13,12 @@
"server/**/*",
],
"references": [
{ "path": "../tsconfig.json" },
{ "path": "../../charts/tsconfig.json" },
{ "path": "../../../core/tsconfig.json" },
{ "path": "../../expressions/tsconfig.json" },
{ "path": "../../data/tsconfig.json"},
{ "path": "../../usage_collection/tsconfig.json" },
{ "path": "../../ui_actions/tsconfig.json" },
{ "path": "../../field_formats/tsconfig.json"},
{ "path": "../../kibana_utils/tsconfig.json" },

View file

@ -0,0 +1,16 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./target/types",
"emitDeclarationOnly": true,
"declaration": true,
"declarationMap": true,
"isolatedModules": true
},
"include": [
"common/**/*"
],
"references": [
{ "path": "../../core/tsconfig.json" },
]
}

View file

@ -231,6 +231,7 @@ export const useDashboardAppState = ({
/**
* Build the dashboard container embeddable, and apply the incoming embeddable if it exists.
*/
const dashboardContainer = await buildDashboardContainer({
...dashboardBuildContext,
initialDashboardState,
@ -238,9 +239,11 @@ export const useDashboardAppState = ({
savedDashboard,
data,
executionContext: {
type: 'dashboard',
description: savedDashboard.title,
},
});
if (canceled || !dashboardContainer) {
tryDestroyDashboardContainer(dashboardContainer);
return;

View file

@ -7,9 +7,9 @@
*/
import { mapValues } from 'lodash';
import { AnyExpressionFunctionDefinition } from '../../types';
import { ExecutionContext } from '../../../execution/types';
import { Datatable } from '../../../expression_types';
import type { AnyExpressionFunctionDefinition } from '../../types';
import type { ExecutionContext } from '../../../execution/types';
import type { Datatable } from '../../../expression_types';
/**
* Takes a function spec and passes in default args,
@ -24,7 +24,9 @@ export const functionWrapper = <
return (
context?: Parameters<ExpressionFunctionDefinition['fn']>[0] | null,
args: Parameters<ExpressionFunctionDefinition['fn']>[1] = {},
handlers: ExecutionContext = {} as ExecutionContext
handlers: ExecutionContext = {
getExecutionContext: jest.fn(),
} as unknown as ExecutionContext
) => spec.fn(context, { ...defaultArgs, ...args }, handlers);
};

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import { ExpressionAstExpression } from '../ast';
export interface ExpressionRenderDefinition<Config = unknown> {
@ -99,4 +100,6 @@ export interface IInterpreterRenderHandlers {
* Downstream consumers of the uiState handler will need to cast for now.
*/
uiState?: unknown;
getExecutionContext(): KibanaExecutionContext | undefined;
}

View file

@ -115,6 +115,7 @@ export class ExpressionLoader {
syncColors: params?.syncColors,
syncTooltips: params?.syncTooltips,
hasCompatibleActions: params?.hasCompatibleActions,
executionContext: params?.executionContext,
});
this.render$ = this.renderHandler.render$;
this.update$ = this.renderHandler.update$;

View file

@ -7,8 +7,8 @@
*/
import { pick } from 'lodash';
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { SerializableRecord } from '@kbn/utility-types';
import type { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import type { SerializableRecord } from '@kbn/utility-types';
import type { ExpressionsServiceSetup, ExpressionsServiceStart } from '../common';
import {
ExpressionsService,

View file

@ -11,6 +11,8 @@ import { Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import { isNumber } from 'lodash';
import { SerializableRecord } from '@kbn/utility-types';
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import {
ExpressionRenderError,
RenderErrorHandlerFnType,
@ -31,6 +33,7 @@ export interface ExpressionRenderHandlerParams {
syncTooltips?: boolean;
interactive?: boolean;
hasCompatibleActions?: (event: ExpressionRendererEvent) => Promise<boolean>;
executionContext?: KibanaExecutionContext;
}
type UpdateValue = IInterpreterRenderUpdateParams<IExpressionLoaderParams>;
@ -58,6 +61,7 @@ export class ExpressionRenderHandler {
syncTooltips,
interactive,
hasCompatibleActions = async () => false,
executionContext,
}: ExpressionRenderHandlerParams = {}
) {
this.element = element;
@ -84,6 +88,9 @@ export class ExpressionRenderHandler {
reload: () => {
this.updateSubject.next(null);
},
getExecutionContext() {
return executionContext;
},
update: (params: UpdateValue) => {
this.updateSubject.next(params);
},

View file

@ -6,9 +6,9 @@
* Side Public License, v 1.
*/
import { NotificationsStart } from '@kbn/core/public';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
import { ExpressionsService, ExpressionRendererRegistry } from '../../common';
import type { NotificationsStart } from '@kbn/core/public';
import type { ExpressionsService, ExpressionRendererRegistry } from '../../common';
export const [getNotifications, setNotifications] =
createGetterSetter<NotificationsStart>('Notifications');

View file

@ -11,6 +11,7 @@
{ "path": "../../core/tsconfig.json" },
{ "path": "../kibana_utils/tsconfig.json" },
{ "path": "../inspector/tsconfig.json" },
{ "path": "../field_formats/tsconfig.json" }
{ "path": "../field_formats/tsconfig.json" },
{ "path": "../usage_collection/tsconfig.json" }
]
}

View file

@ -15,6 +15,7 @@ export const defaultHandlers: IInterpreterRenderHandlers = {
isSyncColorsEnabled: () => false,
isSyncTooltipsEnabled: () => false,
isInteractive: () => true,
getExecutionContext: () => undefined,
done: action('done'),
onDestroy: action('onDestroy'),
reload: action('reload'),

View file

@ -34,7 +34,7 @@ export const getGoalVisTypeDefinition = (
addTooltip: true,
addLegend: false,
isDisplayWarning: false,
type: 'gauge',
type: 'goal',
gauge: {
verticalSplit: false,
autoExtend: false,

View file

@ -33,6 +33,7 @@ describe('TableVisualizationComponent', () => {
},
tables: [],
};
const renderComplete = jest.fn();
const visConfig = {} as unknown as TableVisConfig;
it('should render the basic table', () => {
@ -42,6 +43,7 @@ describe('TableVisualizationComponent', () => {
handlers={handlers}
visData={visData}
visConfig={visConfig}
renderComplete={renderComplete}
/>
);
expect(useUiState).toHaveBeenLastCalledWith(handlers.uiState);
@ -58,6 +60,7 @@ describe('TableVisualizationComponent', () => {
tables: [],
}}
visConfig={visConfig}
renderComplete={renderComplete}
/>
);
expect(useUiState).toHaveBeenLastCalledWith(handlers.uiState);

View file

@ -9,7 +9,6 @@
import './table_visualization.scss';
import React, { useLayoutEffect } from 'react';
import classNames from 'classnames';
import { CoreStart } from '@kbn/core/public';
import { IInterpreterRenderHandlers } from '@kbn/expressions-plugin';
import type { PersistedState } from '@kbn/visualizations-plugin/public';
@ -22,6 +21,7 @@ import { useUiState } from '../utils';
interface TableVisualizationComponentProps {
core: CoreStart;
handlers: IInterpreterRenderHandlers;
renderComplete: () => void;
visData: TableVisData;
visConfig: TableVisConfig;
}
@ -31,13 +31,14 @@ const TableVisualizationComponent = ({
handlers,
visData: { direction, table, tables },
visConfig,
renderComplete,
}: TableVisualizationComponentProps) => {
useLayoutEffect(() => {
// Temporary solution: DataGrid should provide onRender callback
setTimeout(() => {
handlers.done();
renderComplete();
}, 300);
}, [handlers]);
}, [renderComplete]);
const uiStateProps = useUiState(handlers.uiState as PersistedState);

View file

@ -9,7 +9,7 @@
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/public';
import { VisualizationsSetup } from '@kbn/visualizations-plugin/public';
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import { UsageCollectionSetup, UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { setFormatService } from './services';
@ -25,6 +25,7 @@ export interface TablePluginSetupDependencies {
/** @internal */
export interface TablePluginStartDependencies {
data: DataPublicPluginStart;
usageCollection?: UsageCollectionStart;
}
/** @internal */

View file

@ -16,8 +16,8 @@ export const registerTableVis = async (
core: CoreSetup<TablePluginStartDependencies>,
{ expressions, visualizations }: TablePluginSetupDependencies
) => {
const [coreStart] = await core.getStartServices();
const [coreStart, { usageCollection }] = await core.getStartServices();
expressions.registerFunction(createTableVisFn);
expressions.registerRenderer(getTableVisRenderer(coreStart));
expressions.registerRenderer(getTableVisRenderer(coreStart, usageCollection));
visualizations.createBaseVisualization(tableVisTypeDefinition);
};

View file

@ -7,7 +7,11 @@
*/
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
export const [getFormatService, setFormatService] =
createGetterSetter<DataPublicPluginStart['fieldFormats']>('table data.fieldFormats');
export const [getUsageCollectionStart, setUsageCollectionStart] =
createGetterSetter<UsageCollectionStart>('UsageCollection', false);

View file

@ -9,17 +9,34 @@
import React, { lazy } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { CoreStart } from '@kbn/core/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { CoreStart, KibanaExecutionContext } from '@kbn/core/public';
import { VisualizationContainer } from '@kbn/visualizations-plugin/public';
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { TableVisRenderValue } from './table_vis_fn';
const TableVisualizationComponent = lazy(() => import('./components/table_visualization'));
/** @internal **/
const extractContainerType = (context?: KibanaExecutionContext): string | undefined => {
if (context) {
const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => {
if (item.type) {
return item;
} else if (item.child) {
return recursiveGet(item.child);
}
};
return recursiveGet(context)?.type;
}
};
export const getTableVisRenderer: (
core: CoreStart
) => ExpressionRenderDefinition<TableVisRenderValue> = (core) => ({
core: CoreStart,
usageCollection?: UsageCollectionStart
) => ExpressionRenderDefinition<TableVisRenderValue> = (core, usageCollection) => ({
name: 'table_vis',
reuseDomNode: true,
render: async (domNode, { visData, visConfig }, handlers) => {
@ -30,11 +47,28 @@ export const getTableVisRenderer: (
const showNoResult =
visData.table?.rows.length === 0 || (!visData.table && visData.tables.length === 0);
const renderCompete = () => {
const containerType = extractContainerType(handlers.getExecutionContext());
const visualizationType = 'agg_based';
if (usageCollection && containerType) {
const counterEvents = [
`render_${visualizationType}_table`,
!visData.table ? `render_${visualizationType}_table_split` : undefined,
].filter(Boolean) as string[];
usageCollection.reportUiCounter(containerType, METRIC_TYPE.COUNT, counterEvents);
}
handlers.done();
};
render(
<KibanaThemeProvider theme$={core.theme.theme$}>
<VisualizationContainer
data-test-subj="tbvChartContainer"
handlers={handlers}
renderComplete={renderCompete}
showNoResult={showNoResult}
>
<TableVisualizationComponent
@ -42,6 +76,7 @@ export const getTableVisRenderer: (
handlers={handlers}
visData={visData}
visConfig={visConfig}
renderComplete={renderCompete}
/>
</VisualizationContainer>
</KibanaThemeProvider>,

View file

@ -6,6 +6,7 @@
"ui": true,
"requiredPlugins": ["visualizations", "data", "expressions", "charts", "dataViews", "fieldFormats"],
"requiredBundles": ["kibanaUtils", "kibanaReact", "visDefaultEditor"],
"optionalPlugins": ["usageCollection"],
"owner": {
"name": "Vis Editors",
"githubTeam": "kibana-vis-editors"

View file

@ -9,7 +9,8 @@
import type { ISearchStart } from '@kbn/data-plugin/public';
import type { DataViewsContract } from '@kbn/data-views-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
export const [getIndexPatterns, setIndexPatterns] =
@ -21,3 +22,8 @@ export const [getCharts, setCharts] = createGetterSetter<ChartsPluginStart>('Cha
export const [getFieldFormats, setFieldFormats] =
createGetterSetter<FieldFormatsStart>('FieldFormats');
export const [getUsageCollection, setUsageCollection] = createGetterSetter<UsageCollectionStart>(
'UsageCollection',
false
);

View file

@ -26,6 +26,7 @@ import type { VisualizationsSetup } from '@kbn/visualizations-plugin/public';
import type { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public';
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { getTimelionVisualizationConfig } from './timelion_vis_fn';
import { getTimelionVisDefinition } from './timelion_vis_type';
import {
@ -33,6 +34,7 @@ import {
setDataSearch,
setCharts,
setFieldFormats,
setUsageCollection,
} from './helpers/plugin_services';
import { getArgValueSuggestions } from './helpers/arg_value_suggestions';
@ -62,6 +64,7 @@ export interface TimelionVisStartDependencies {
dataViews: DataViewsPublicPluginStart;
charts: ChartsPluginStart;
fieldFormats: FieldFormatsStart;
usageCollection?: UsageCollectionStart;
}
/** @public */
@ -99,13 +102,17 @@ export class TimelionVisPlugin
public start(
core: CoreStart,
{ data, charts, dataViews, fieldFormats }: TimelionVisStartDependencies
{ data, charts, dataViews, fieldFormats, usageCollection }: TimelionVisStartDependencies
) {
setIndexPatterns(dataViews);
setDataSearch(data.search);
setCharts(charts);
setFieldFormats(fieldFormats);
if (usageCollection) {
setUsageCollection(usageCollection);
}
return {
getArgValueSuggestions,
};

View file

@ -13,12 +13,30 @@ import { ExpressionRenderDefinition } from '@kbn/expressions-plugin';
import { RangeFilterParams } from '@kbn/es-query';
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { VisualizationContainer } from '@kbn/visualizations-plugin/public';
import { METRIC_TYPE } from '@kbn/analytics';
import { KibanaExecutionContext } from '@kbn/core/public';
import { TimelionVisDependencies } from './plugin';
import { TimelionRenderValue } from './timelion_vis_fn';
import { getUsageCollection } from './helpers/plugin_services';
const LazyTimelionVisComponent = lazy(() =>
import('./async_services').then(({ TimelionVisComponent }) => ({ default: TimelionVisComponent }))
);
/** @internal **/
const extractContainerType = (context?: KibanaExecutionContext): string | undefined => {
if (context) {
const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => {
if (item.type) {
return item;
} else if (item.child) {
return recursiveGet(item.child);
}
};
return recursiveGet(context)?.type;
}
};
export const getTimelionVisRenderer: (
deps: TimelionVisDependencies
) => ExpressionRenderDefinition<TimelionRenderValue> = (deps) => ({
@ -51,8 +69,26 @@ export const getTimelionVisRenderer: (
});
};
const renderComplete = () => {
const usageCollection = getUsageCollection();
const containerType = extractContainerType(handlers.getExecutionContext());
if (usageCollection && containerType) {
usageCollection.reportUiCounter(
containerType,
METRIC_TYPE.COUNT,
`render_agg_based_timelion`
);
}
handlers.done();
};
render(
<VisualizationContainer handlers={handlers} showNoResult={showNoResult}>
<VisualizationContainer
renderComplete={renderComplete}
handlers={handlers}
showNoResult={showNoResult}
>
<KibanaThemeProvider theme$={deps.theme.theme$}>
<KibanaContextProvider services={{ ...deps }}>
{seriesList && (
@ -60,7 +96,7 @@ export const getTimelionVisRenderer: (
interval={visParams.interval}
ariaLabel={visParams.ariaLabel}
seriesList={seriesList}
renderComplete={handlers.done}
renderComplete={renderComplete}
onBrushEvent={onBrushEvent}
syncTooltips={syncTooltips}
/>

View file

@ -12,6 +12,7 @@ import { VisualizationsSetup } from '@kbn/visualizations-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { ChartsPluginStart } from '@kbn/charts-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { EditorController, TSVB_EDITOR_NAME } from './application/editor_controller';
import { createMetricsFn } from './metrics_fn';
@ -24,6 +25,7 @@ import {
setDataStart,
setDataViewsStart,
setCharts,
setUsageCollectionStart,
} from './services';
import { getTimeseriesVisRenderer } from './timeseries_vis_renderer';
@ -38,6 +40,7 @@ export interface MetricsPluginStartDependencies {
data: DataPublicPluginStart;
dataViews: DataViewsPublicPluginStart;
charts: ChartsPluginStart;
usageCollection?: UsageCollectionStart;
}
/** @internal */
@ -61,12 +64,18 @@ export class MetricsPlugin implements Plugin<void, void> {
visualizations.createBaseVisualization(metricsVisDefinition);
}
public start(core: CoreStart, { data, charts, dataViews }: MetricsPluginStartDependencies) {
public start(
core: CoreStart,
{ data, charts, dataViews, usageCollection }: MetricsPluginStartDependencies
) {
setCharts(charts);
setI18n(core.i18n);
setFieldFormats(data.fieldFormats);
setDataStart(data);
setDataViewsStart(dataViews);
setCoreStart(core);
if (usageCollection) {
setUsageCollectionStart(usageCollection);
}
}
}

View file

@ -6,11 +6,12 @@
* Side Public License, v 1.
*/
import { I18nStart, IUiSettingsClient, CoreStart } from '@kbn/core/public';
import type { I18nStart, IUiSettingsClient, CoreStart } from '@kbn/core/public';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
import { ChartsPluginStart } from '@kbn/charts-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { ChartsPluginStart } from '@kbn/charts-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
export const [getUISettings, setUISettings] = createGetterSetter<IUiSettingsClient>('UISettings');
@ -27,3 +28,6 @@ export const [getDataViewsStart, setDataViewsStart] =
export const [getI18n, setI18n] = createGetterSetter<I18nStart>('I18n');
export const [getCharts, setCharts] = createGetterSetter<ChartsPluginStart>('ChartsPluginStart');
export const [getUsageCollectionStart, setUsageCollectionStart] =
createGetterSetter<UsageCollectionStart>('UsageCollection', false);

View file

@ -9,14 +9,16 @@
import React, { lazy } from 'react';
import { get } from 'lodash';
import { render, unmountComponentAtNode } from 'react-dom';
import { METRIC_TYPE } from '@kbn/analytics';
import { I18nProvider } from '@kbn/i18n-react';
import { IUiSettingsClient, ThemeServiceStart } from '@kbn/core/public';
import { IUiSettingsClient, KibanaExecutionContext, ThemeServiceStart } from '@kbn/core/public';
import { VisualizationContainer, PersistedState } from '@kbn/visualizations-plugin/public';
import type { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { getUsageCollectionStart } from './services';
import { TIME_RANGE_DATA_MODES } from '../common/enums';
import type { TimeseriesVisData } from '../common/types';
import { isVisTableData } from '../common/vis_data_utils';
@ -36,6 +38,20 @@ const checkIfDataExists = (visData: TimeseriesVisData | {}, model: TimeseriesVis
return false;
};
/** @internal **/
const extractContainerType = (context?: KibanaExecutionContext): string | undefined => {
if (context) {
const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => {
if (item.type) {
return item;
} else if (item.child) {
return recursiveGet(item.child);
}
};
return recursiveGet(context)?.type;
}
};
export const getTimeseriesVisRenderer: (deps: {
uiSettings: IUiSettingsClient;
theme: ThemeServiceStart;
@ -49,12 +65,39 @@ export const getTimeseriesVisRenderer: (deps: {
const { visParams: model, visData, syncColors, syncTooltips } = config;
const showNoResult = !checkIfDataExists(visData, model);
const { triggerTSVBtoLensConfiguration } = await import('./trigger_action');
const canNavigateToLens = await triggerTSVBtoLensConfiguration(model);
const renderComplete = () => {
const usageCollection = getUsageCollectionStart();
const containerType = extractContainerType(handlers.getExecutionContext());
const visualizationType = 'tsvb';
if (usageCollection && containerType) {
const counterEvents = [
`render_${visualizationType}_${model.type}`,
model.use_kibana_indexes === false
? `render_${visualizationType}_index_pattern_string`
: undefined,
model.time_range_mode === TIME_RANGE_DATA_MODES.LAST_VALUE
? `render_${visualizationType}_last_value`
: undefined,
canNavigateToLens ? `render_${visualizationType}_convertable` : undefined,
].filter(Boolean) as string[];
usageCollection.reportUiCounter(containerType, METRIC_TYPE.COUNT, counterEvents);
}
handlers.done();
};
render(
<I18nProvider>
<KibanaThemeProvider theme$={theme.theme$}>
<VisualizationContainer
data-test-subj="timeseriesVis"
handlers={handlers}
renderComplete={renderComplete}
showNoResult={showNoResult}
error={get(visData, [model.id, 'error'])}
>
@ -67,9 +110,7 @@ export const getTimeseriesVisRenderer: (deps: {
syncColors={syncColors}
syncTooltips={syncTooltips}
uiState={handlers.uiState! as PersistedState}
initialRender={() => {
handlers.done();
}}
initialRender={renderComplete}
/>
</VisualizationContainer>
</KibanaThemeProvider>

View file

@ -14,6 +14,7 @@ import { VisualizationsSetup } from '@kbn/visualizations-plugin/public';
import { Setup as InspectorSetup } from '@kbn/inspector-plugin/public';
import type { MapsEmsPluginPublicStart } from '@kbn/maps-ems-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import {
setNotifications,
setData,
@ -22,6 +23,7 @@ import {
setUISettings,
setDocLinks,
setMapsEms,
setUsageCollectionStart,
} from './services';
import { createVegaFn } from './vega_fn';
@ -56,6 +58,7 @@ export interface VegaPluginStartDependencies {
data: DataPublicPluginStart;
mapsEms: MapsEmsPluginPublicStart;
dataViews: DataViewsPublicPluginStart;
usageCollection?: UsageCollectionStart;
}
/** @internal */
@ -92,11 +95,18 @@ export class VegaPlugin implements Plugin<void, void> {
visualizations.createBaseVisualization(createVegaTypeDefinition());
}
public start(core: CoreStart, { data, mapsEms, dataViews }: VegaPluginStartDependencies) {
public start(
core: CoreStart,
{ data, mapsEms, dataViews, usageCollection }: VegaPluginStartDependencies
) {
setNotifications(core.notifications);
setData(data);
setDataViews(dataViews);
setDocLinks(core.docLinks);
setMapsEms(mapsEms);
if (usageCollection) {
setUsageCollectionStart(usageCollection);
}
}
}

View file

@ -12,6 +12,7 @@ import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
import type { MapsEmsPluginPublicStart } from '@kbn/maps-ems-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
export const [getData, setData] = createGetterSetter<DataPublicPluginStart>('Data');
@ -31,3 +32,6 @@ export const [getInjectedVars, setInjectedVars] = createGetterSetter<{
export const getEnableExternalUrls = () => getInjectedVars().enableExternalUrls;
export const [getDocLinks, setDocLinks] = createGetterSetter<DocLinksStart>('docLinks');
export const [getUsageCollectionStart, setUsageCollectionStart] =
createGetterSetter<UsageCollectionStart>('UsageCollection', false);

View file

@ -8,16 +8,32 @@
import React, { lazy } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { METRIC_TYPE } from '@kbn/analytics';
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { VisualizationContainer } from '@kbn/visualizations-plugin/public';
import { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import { VegaVisualizationDependencies } from './plugin';
import { getUsageCollectionStart } from './services';
import { RenderValue } from './vega_fn';
const LazyVegaVisComponent = lazy(() =>
import('./async_services').then(({ VegaVisComponent }) => ({ default: VegaVisComponent }))
);
/** @internal **/
const extractContainerType = (context?: KibanaExecutionContext): string | undefined => {
if (context) {
const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => {
if (item.type) {
return item;
} else if (item.child) {
return recursiveGet(item.child);
}
};
return recursiveGet(context)?.type;
}
};
export const getVegaVisRenderer: (
deps: VegaVisualizationDependencies
) => ExpressionRenderDefinition<RenderValue> = (deps) => ({
@ -27,13 +43,32 @@ export const getVegaVisRenderer: (
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
const renderComplete = () => {
const usageCollection = getUsageCollectionStart();
const containerType = extractContainerType(handlers.getExecutionContext());
const visualizationType = 'vega';
if (usageCollection && containerType) {
const counterEvents = [
`render_${visualizationType}`,
visData.useMap ? `render_${visualizationType}_map` : undefined,
`render_${visualizationType}_${visData.isVegaLite ? 'lite' : 'normal'}`,
].filter(Boolean) as string[];
usageCollection.reportUiCounter(containerType, METRIC_TYPE.COUNT, counterEvents);
}
handlers.done();
};
render(
<KibanaThemeProvider theme$={deps.core.theme.theme$}>
<VisualizationContainer handlers={handlers}>
<LazyVegaVisComponent
deps={deps}
fireEvent={handlers.event}
renderComplete={handlers.done}
renderComplete={renderComplete}
renderMode={handlers.getRenderMode()}
visData={visData}
/>

View file

@ -4,6 +4,7 @@
"server": true,
"ui": true,
"requiredPlugins": ["charts", "data", "expressions", "visualizations"],
"optionalPlugins": ["usageCollection"],
"requiredBundles": ["kibanaUtils", "visTypeXy", "visTypePie", "visTypeHeatmap", "visTypeGauge", "fieldFormats", "kibanaReact"],
"owner": {
"name": "Vis Editors",

View file

@ -15,6 +15,8 @@ import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { LEGACY_PIE_CHARTS_LIBRARY } from '@kbn/vis-type-pie-plugin/common';
import { LEGACY_HEATMAP_CHARTS_LIBRARY } from '@kbn/vis-type-heatmap-plugin/common';
import { LEGACY_GAUGE_CHARTS_LIBRARY } from '@kbn/vis-type-gauge-plugin/common';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { setUsageCollectionStart } from './services';
import { heatmapVisTypeDefinition } from './heatmap';
import { createVisTypeVislibVisFn } from './vis_type_vislib_vis_fn';
@ -35,6 +37,7 @@ export interface VisTypeVislibPluginSetupDependencies {
/** @internal */
export interface VisTypeVislibPluginStartDependencies {
data: DataPublicPluginStart;
usageCollection?: UsageCollectionStart;
}
export type VisTypeVislibCoreSetup = CoreSetup<VisTypeVislibPluginStartDependencies, void>;
@ -73,9 +76,12 @@ export class VisTypeVislibPlugin
}
}
public start(core: CoreStart, { data }: VisTypeVislibPluginStartDependencies) {
public start(core: CoreStart, { data, usageCollection }: VisTypeVislibPluginStartDependencies) {
setFormatService(data.fieldFormats);
setDataActions(data.actions);
setTheme(core.theme);
if (usageCollection) {
setUsageCollectionStart(usageCollection);
}
}
}

View file

@ -9,6 +9,7 @@
import { ThemeServiceStart } from '@kbn/core/public';
import { createGetterSetter } from '@kbn/kibana-utils-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
export const [getDataActions, setDataActions] =
createGetterSetter<DataPublicPluginStart['actions']>('vislib data.actions');
@ -18,3 +19,6 @@ export const [getFormatService, setFormatService] = createGetterSetter<
>('vislib data.fieldFormats');
export const [getTheme, setTheme] = createGetterSetter<ThemeServiceStart>('vislib theme service');
export const [getUsageCollectionStart, setUsageCollectionStart] =
createGetterSetter<UsageCollectionStart>('UsageCollection', false);

View file

@ -9,10 +9,13 @@
import $ from 'jquery';
import React, { RefObject } from 'react';
import { METRIC_TYPE } from '@kbn/analytics';
import { mountReactNode } from '@kbn/core/public/utils';
import { ChartsPluginSetup } from '@kbn/charts-plugin/public';
import type { PersistedState } from '@kbn/visualizations-plugin/public';
import { IInterpreterRenderHandlers } from '@kbn/expressions-plugin/public';
import { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import { getUsageCollectionStart } from './services';
import { VisTypeVislibCoreSetup } from './plugin';
import { VisLegend, CUSTOM_LEGEND_VIS_TYPES } from './vislib/components/legend';
import { BasicVislibParams } from './types';
@ -27,6 +30,38 @@ const legendClassName = {
export type VislibVisController = InstanceType<ReturnType<typeof createVislibVisController>>;
/** @internal **/
const extractContainerType = (context?: KibanaExecutionContext): string | undefined => {
if (context) {
const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => {
if (item.type) {
return item;
} else if (item.child) {
return recursiveGet(item.child);
}
};
return recursiveGet(context)?.type;
}
};
const renderComplete = (
visParams: BasicVislibParams | PieVisParams,
handlers: IInterpreterRenderHandlers
) => {
const usageCollection = getUsageCollectionStart();
const containerType = extractContainerType(handlers.getExecutionContext());
if (usageCollection && containerType) {
usageCollection.reportUiCounter(
containerType,
METRIC_TYPE.COUNT,
`render_agg_based_${visParams.type}`
);
}
handlers.done();
};
export const createVislibVisController = (
core: VisTypeVislibCoreSetup,
charts: ChartsPluginSetup
@ -74,7 +109,7 @@ export const createVislibVisController = (
this.chartEl.dataset.vislibChartType = visParams.type;
if (this.el.clientWidth === 0 || this.el.clientHeight === 0) {
handlers.done();
renderComplete(visParams, handlers);
return;
}
@ -98,7 +133,7 @@ export const createVislibVisController = (
this.mountLegend(esResponse, visParams, fireEvent, uiState as PersistedState);
}
handlers.done();
renderComplete(visParams, handlers);
});
this.removeListeners = () => {

View file

@ -3,8 +3,9 @@
"version": "kibana",
"ui": true,
"server": true,
"requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection"],
"requiredPlugins": ["charts", "data", "expressions", "visualizations"],
"requiredBundles": ["kibanaUtils", "visDefaultEditor", "kibanaReact"],
"optionalPlugins": ["usageCollection"],
"extraPublicDirs": ["common/index"],
"owner": {
"name": "Vis Editors",

View file

@ -11,7 +11,8 @@ import { Plugin as ExpressionsPublicPlugin } from '@kbn/expressions-plugin/publi
import { VisualizationsSetup, VisualizationsStart } from '@kbn/visualizations-plugin/public';
import { ChartsPluginSetup, ChartsPluginStart } from '@kbn/charts-plugin/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
import { UsageCollectionSetup, UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
import { createStartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import {
setDataActions,
setFormatService,
@ -46,6 +47,7 @@ export interface VisTypeXyPluginStartDependencies {
visualizations: VisualizationsStart;
data: DataPublicPluginStart;
charts: ChartsPluginStart;
usageCollection?: UsageCollectionStart;
}
type VisTypeXyCoreSetup = CoreSetup<VisTypeXyPluginStartDependencies, VisTypeXyPluginStart>;
@ -68,10 +70,14 @@ export class VisTypeXyPlugin
setThemeService(charts.theme);
setPalettesService(charts.palettes);
const getStartDeps = createStartServicesGetter<
VisTypeXyPluginStartDependencies,
VisTypeXyPluginStart
>(core.getStartServices);
expressions.registerRenderer(
getXYVisRenderer({
uiSettings: core.uiSettings,
theme: core.theme,
getStartDeps,
})
);
expressions.registerFunction(expressionFunctions.visTypeXyVisFn);

View file

@ -9,17 +9,20 @@
import React, { lazy } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { I18nProvider } from '@kbn/i18n-react';
import { IUiSettingsClient, ThemeServiceStart } from '@kbn/core/public';
import { KibanaExecutionContext } from '@kbn/core-execution-context-common';
import { METRIC_TYPE } from '@kbn/analytics';
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
import { VisualizationContainer } from '@kbn/visualizations-plugin/public';
import type { PersistedState } from '@kbn/visualizations-plugin/public';
import type { ExpressionRenderDefinition } from '@kbn/expressions-plugin/public';
import { LEGACY_TIME_AXIS } from '@kbn/charts-plugin/common';
import { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
import type { XyVisType } from '../common';
import type { VisComponentType } from './vis_component';
import { RenderValue, visName } from './expression_functions/xy_vis_fn';
import { VisTypeXyPluginStartDependencies } from './plugin';
// @ts-ignore
const VisComponent = lazy<VisComponentType>(() => import('./vis_component'));
@ -31,30 +34,70 @@ function shouldShowNoResultsMessage(visData: any, visType: XyVisType): boolean {
return Boolean(isZeroHits);
}
/** @internal **/
const extractContainerType = (context?: KibanaExecutionContext): string | undefined => {
if (context) {
const recursiveGet = (item: KibanaExecutionContext): KibanaExecutionContext | undefined => {
if (item.type) {
return item;
} else if (item.child) {
return recursiveGet(item.child);
}
};
return recursiveGet(context)?.type;
}
};
export const getXYVisRenderer: (deps: {
uiSettings: IUiSettingsClient;
theme: ThemeServiceStart;
}) => ExpressionRenderDefinition<RenderValue> = ({ uiSettings, theme }) => ({
getStartDeps: StartServicesGetter<VisTypeXyPluginStartDependencies>;
}) => ExpressionRenderDefinition<RenderValue> = ({ getStartDeps }) => ({
name: visName,
displayName: 'XY visualization',
reuseDomNode: true,
render: async (domNode, { visData, visConfig, visType, syncColors, syncTooltips }, handlers) => {
const { core, plugins } = getStartDeps();
const showNoResult = shouldShowNoResultsMessage(visData, visType);
const renderComplete = () => {
// Renaming according to business requirements
const visTypeTelemetryMap: Record<string, string> = {
histogram: 'vertical_bar',
};
const containerType = extractContainerType(handlers.getExecutionContext());
const visualizationType = 'agg_based';
if (plugins.usageCollection && containerType) {
const hasMixedXY = new Set(visConfig.seriesParams.map((item) => item.type));
const counterEvents = [
`render_${visualizationType}_${visTypeTelemetryMap[visType] ?? visType}`,
hasMixedXY.size > 1 ? `render_${visualizationType}_mixed_xy` : undefined,
].filter(Boolean) as string[];
plugins.usageCollection.reportUiCounter(containerType, METRIC_TYPE.COUNT, counterEvents);
}
handlers.done();
};
handlers.onDestroy(() => unmountComponentAtNode(domNode));
render(
<KibanaThemeProvider theme$={theme.theme$}>
<KibanaThemeProvider theme$={core.theme.theme$}>
<I18nProvider>
<VisualizationContainer handlers={handlers} showNoResult={showNoResult}>
<VisualizationContainer
renderComplete={renderComplete}
handlers={handlers}
showNoResult={showNoResult}
>
<VisComponent
visParams={visConfig}
visData={visData}
renderComplete={handlers.done}
renderComplete={renderComplete}
fireEvent={handlers.event}
uiState={handlers.uiState as PersistedState}
syncColors={syncColors}
syncTooltips={syncTooltips}
useLegacyTimeAxis={uiSettings.get(LEGACY_TIME_AXIS, false)}
useLegacyTimeAxis={core.uiSettings.get(LEGACY_TIME_AXIS, false)}
/>
</VisualizationContainer>
</I18nProvider>

View file

@ -17,6 +17,7 @@ export interface VisualizationContainerProps {
className?: string;
children: ReactNode;
handlers: IInterpreterRenderHandlers;
renderComplete?: () => void;
showNoResult?: boolean;
error?: string;
}
@ -31,6 +32,7 @@ export const VisualizationContainer = ({
handlers,
showNoResult = false,
error,
renderComplete,
}: VisualizationContainerProps) => {
const classes = classNames('visualization', className);
@ -46,7 +48,9 @@ export const VisualizationContainer = ({
{error ? (
<VisualizationError onInit={() => handlers.done()} error={error} />
) : showNoResult ? (
<VisualizationNoResults onInit={() => handlers.done()} />
<VisualizationNoResults
onInit={() => (renderComplete ? renderComplete() : handlers.done())}
/>
) : (
children
)}

View file

@ -361,6 +361,7 @@ export class VisualizeEmbeddable
onRenderError: (element: HTMLElement, error: ExpressionRenderError) => {
this.onContainerError(error);
},
executionContext: this.getExecutionContext(),
});
this.subscriptions.push(
@ -464,19 +465,24 @@ export class VisualizeEmbeddable
await this.handleVisUpdate();
};
private async updateHandler() {
private getExecutionContext() {
const parentContext = this.parent?.getInput().executionContext || getExecutionContext().get();
const child: KibanaExecutionContext = {
type: 'visualization',
type: 'agg_based',
name: this.vis.type.name,
id: this.vis.id ?? 'new',
description: this.vis.title || this.input.title || this.vis.type.name,
url: this.output.editUrl,
};
const context = {
return {
...parentContext,
child,
};
}
private async updateHandler() {
const context = this.getExecutionContext();
const expressionParams: IExpressionLoaderParams = {
searchContext: {

View file

@ -30,6 +30,9 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
name: 'execution_context_app',
// add a non-ASCII symbols to make sure it doesn't break the context propagation mechanism
id: 'Visualization☺漢字',
meta: {
foo: 'какая-то странная мета',
},
description: 'какое-то странное описание',
};

View file

@ -27,6 +27,7 @@ export const defaultHandlers: RendererHandlers = {
reload: action('reload'),
update: action('update'),
event: action('event'),
getExecutionContext: () => undefined,
};
/*

View file

@ -99,7 +99,13 @@ export const embeddableRendererFactory = (
throw new EmbeddableFactoryNotFoundError(embeddableType);
}
const embeddableInput = { ...input, id: uniqueId };
const embeddableInput = {
...input,
id: uniqueId,
executionContext: {
type: 'canvas',
},
};
const embeddablePromise = input.savedObjectId
? factory

View file

@ -29,6 +29,7 @@ export const createBaseHandlers = (): IInterpreterRenderHandlers => ({
isSyncColorsEnabled: () => false,
isSyncTooltipsEnabled: () => false,
isInteractive: () => true,
getExecutionContext: () => undefined,
});
export const createHandlers = (baseHandlers = createBaseHandlers()): RendererHandlers => ({

Some files were not shown because too many files have changed in this diff Show more