Skip to content

Commit

Permalink
[Drilldowns] {{event.points}} in URL drilldown for VALUE_CLICK_TRIGGER (
Browse files Browse the repository at this point in the history
#76771)

{{event.points}} in URL drilldown for VALUE_CLICK_TRIGGER

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
Dosant and elasticmachine committed Sep 17, 2020
1 parent 4c734e4 commit d398dbf
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 171 deletions.
17 changes: 17 additions & 0 deletions docs/user/dashboard/url-drilldown.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ context.panel.timeRange.indexPatternIds
| ID of saved object behind a panel.

| *Single click*

| event.value
| Value behind clicked data point.

Expand All @@ -208,6 +209,22 @@ context.panel.timeRange.indexPatternIds
| event.negate
| Boolean, indicating whether clicked data point resulted in negative filter.

|
| event.points
| Some visualizations have clickable points that emit more than one data point. Use list of data points in case a single value is insufficient. +

Example:

`{{json event.points}}` +
`{{event.points.[0].key}}` +
`{{event.points.[0].value}}`
`{{#each event.points}}key=value&{{/each}}`

Note:

`{{event.value}}` is a shorthand for `{{event.points.[0].value}}` +
`{{event.key}}` is a shorthand for `{{event.points.[0].key}}`

| *Range selection*
| event.from +
event.to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'> = {
defaultMessage: 'Single click',
}),
description: i18n.translate('uiActions.triggers.valueClickDescription', {
defaultMessage: 'A single point on the visualization',
defaultMessage: 'A data point click on the visualization',
}),
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

import { UrlDrilldown, ActionContext, Config } from './url_drilldown';
import { coreMock } from '../../../../../../src/core/public/mocks';
import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public/lib/embeddables';

const mockDataPoints = [
Expand Down Expand Up @@ -52,7 +51,6 @@ const mockNavigateToUrl = jest.fn(() => Promise.resolve());
describe('UrlDrilldown', () => {
const urlDrilldown = new UrlDrilldown({
getGlobalScope: () => ({ kibanaUrl: 'http://localhost:5601/' }),
getOpenModal: () => Promise.resolve(coreMock.createStart().overlays.openModal),
getSyntaxHelpDocsLink: () => 'http://localhost:5601/docs',
getVariablesHelpDocsLink: () => 'http://localhost:5601/docs',
navigateToUrl: mockNavigateToUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

import React from 'react';
import { OverlayStart } from 'kibana/public';
import { reactToUiComponent } from '../../../../../../src/plugins/kibana_react/public';
import { ChartActionContext, IEmbeddable } from '../../../../../../src/plugins/embeddable/public';
import { CollectConfigProps as CollectConfigPropsBase } from '../../../../../../src/plugins/kibana_utils/public';
Expand All @@ -29,7 +28,6 @@ import { txtUrlDrilldownDisplayName } from './i18n';
interface UrlDrilldownDeps {
getGlobalScope: () => UrlDrilldownGlobalScope;
navigateToUrl: (url: string) => Promise<void>;
getOpenModal: () => Promise<OverlayStart['openModal']>;
getSyntaxHelpDocsLink: () => string;
getVariablesHelpDocsLink: () => string;
}
Expand Down Expand Up @@ -112,13 +110,10 @@ export class UrlDrilldown implements Drilldown<Config, UrlTrigger, ActionFactory
};

public readonly getHref = async (config: Config, context: ActionContext) =>
urlDrilldownCompileUrl(config.url.template, await this.buildRuntimeScope(context));
urlDrilldownCompileUrl(config.url.template, this.buildRuntimeScope(context));

public readonly execute = async (config: Config, context: ActionContext) => {
const url = await urlDrilldownCompileUrl(
config.url.template,
await this.buildRuntimeScope(context, { allowPrompts: true })
);
const url = urlDrilldownCompileUrl(config.url.template, this.buildRuntimeScope(context));
if (config.openInNewTab) {
window.open(url, '_blank', 'noopener');
} else {
Expand All @@ -134,14 +129,11 @@ export class UrlDrilldown implements Drilldown<Config, UrlTrigger, ActionFactory
});
};

private buildRuntimeScope = async (
context: ActionContext,
opts: { allowPrompts: boolean } = { allowPrompts: false }
) => {
private buildRuntimeScope = (context: ActionContext) => {
return urlDrilldownBuildScope({
globalScope: this.deps.getGlobalScope(),
contextScope: getContextScope(context),
eventScope: await getEventScope(context, this.deps, opts),
eventScope: getEventScope(context),
});
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import {
getEventScope,
getMockEventScope,
ValueClickTriggerEventScope,
} from './url_drilldown_scope';

const createPoint = ({
field,
value,
}: {
field: string;
value: string | null | number | boolean;
}) => ({
table: {
columns: [
{
name: field,
id: '1-1',
meta: {
type: 'histogram',
indexPatternId: 'logstash-*',
aggConfigParams: {
field,
interval: 30,
otherBucket: true,
},
},
},
],
rows: [
{
'1-1': '2048',
},
],
},
column: 0,
row: 0,
value,
});

describe('VALUE_CLICK_TRIGGER', () => {
describe('supports `points[]`', () => {
test('getEventScope()', () => {
const mockDataPoints = [
createPoint({ field: 'field0', value: 'value0' }),
createPoint({ field: 'field1', value: 'value1' }),
createPoint({ field: 'field2', value: 'value2' }),
];

const eventScope = getEventScope({
data: { data: mockDataPoints },
}) as ValueClickTriggerEventScope;

expect(eventScope.key).toBe('field0');
expect(eventScope.value).toBe('value0');
expect(eventScope.points).toHaveLength(mockDataPoints.length);
expect(eventScope.points).toMatchInlineSnapshot(`
Array [
Object {
"key": "field0",
"value": "value0",
},
Object {
"key": "field1",
"value": "value1",
},
Object {
"key": "field2",
"value": "value2",
},
]
`);
});

test('getMockEventScope()', () => {
const mockEventScope = getMockEventScope([
'VALUE_CLICK_TRIGGER',
]) as ValueClickTriggerEventScope;
expect(mockEventScope.points.length).toBeGreaterThan(3);
expect(mockEventScope.points).toMatchInlineSnapshot(`
Array [
Object {
"key": "event.points.0.key",
"value": "event.points.0.value",
},
Object {
"key": "event.points.1.key",
"value": "event.points.1.value",
},
Object {
"key": "event.points.2.key",
"value": "event.points.2.value",
},
Object {
"key": "event.points.3.key",
"value": "event.points.3.value",
},
]
`);
});
});

describe('handles undefined, null or missing values', () => {
test('undefined or missing values are removed from the result scope', () => {
const point = createPoint({ field: undefined } as any);
const eventScope = getEventScope({
data: { data: [point] },
}) as ValueClickTriggerEventScope;

expect('key' in eventScope).toBeFalsy();
expect('value' in eventScope).toBeFalsy();
});

test('null value stays in the result scope', () => {
const point = createPoint({ field: 'field', value: null });
const eventScope = getEventScope({
data: { data: [point] },
}) as ValueClickTriggerEventScope;

expect(eventScope.value).toBeNull();
});
});
});
Loading

0 comments on commit d398dbf

Please sign in to comment.