Skip to content

Commit 60deb7e

Browse files
mbondyraelasticmachinewylieconlon
committed
[Lens] Allow user to drag and select a subset of the timeline in the chart (aka brush interaction) (#62636)
* feat: brushing basic example for time histogram * test: added * refactor: simplify the structure * refactor: move to inline function * refactor * refactor * Always use time field from index pattern * types * use the meta.aggConfigParams for timefieldName * fix: test snapshot update * Update embeddable.tsx removing commented code * fix: moment remov * fix: corrections for adapting to timepicker on every timefield * fix: fix single bar condition * types Co-authored-by: Elastic Machine <[email protected]> Co-authored-by: Wylie Conlon <[email protected]>
1 parent 0152c5e commit 60deb7e

File tree

7 files changed

+253
-25
lines changed

7 files changed

+253
-25
lines changed

x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
9696
public supportedTriggers() {
9797
switch (this.savedVis.visualizationType) {
9898
case 'lnsXY':
99-
// TODO: case 'lnsDatatable':
100-
return [VIS_EVENT_TO_TRIGGER.filter];
101-
99+
return [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush];
102100
case 'lnsMetric':
103101
default:
104102
return [];

x-pack/plugins/lens/public/xy_visualization/services.ts x-pack/plugins/lens/public/services.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import { createGetterSetter } from '../../../../../src/plugins/kibana_utils/public';
8-
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
7+
import { createGetterSetter } from '../../../../src/plugins/kibana_utils/public';
8+
import { UiActionsStart } from '../../../../src/plugins/ui_actions/public';
99

1010
export const [getExecuteTriggerActions, setExecuteTriggerActions] = createGetterSetter<
1111
UiActionsStart['executeTriggerActions']

x-pack/plugins/lens/public/xy_visualization/__snapshots__/xy_expression.test.tsx.snap

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/plugins/lens/public/xy_visualization/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import { xyVisualization } from './xy_visualization';
1212
import { xyChart, getXyChartRenderer } from './xy_expression';
1313
import { legendConfig, xConfig, layerConfig } from './types';
1414
import { EditorFrameSetup, FormatFactory } from '../types';
15+
import { setExecuteTriggerActions } from '../services';
1516
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
16-
import { setExecuteTriggerActions } from './services';
1717

1818
export interface XyVisualizationPluginSetupPlugins {
1919
expressions: ExpressionsSetup;

x-pack/plugins/lens/public/xy_visualization/to_expression.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export const buildExpression = (
142142
.concat(layer.splitAccessor ? [layer.splitAccessor] : [])
143143
.forEach(accessor => {
144144
const operation = datasource.getOperationForColumnId(accessor);
145-
if (operation && operation.label) {
145+
if (operation?.label) {
146146
columnToLabel[accessor] = operation.label;
147147
}
148148
});

x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx

+173-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,145 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';
2727

2828
const executeTriggerActions = jest.fn();
2929

30+
const dateHistogramData: LensMultiTable = {
31+
type: 'lens_multitable',
32+
tables: {
33+
timeLayer: {
34+
type: 'kibana_datatable',
35+
rows: [
36+
{
37+
xAccessorId: 1585758120000,
38+
splitAccessorId: "Men's Clothing",
39+
yAccessorId: 1,
40+
},
41+
{
42+
xAccessorId: 1585758360000,
43+
splitAccessorId: "Women's Accessories",
44+
yAccessorId: 1,
45+
},
46+
{
47+
xAccessorId: 1585758360000,
48+
splitAccessorId: "Women's Clothing",
49+
yAccessorId: 1,
50+
},
51+
{
52+
xAccessorId: 1585759380000,
53+
splitAccessorId: "Men's Clothing",
54+
yAccessorId: 1,
55+
},
56+
{
57+
xAccessorId: 1585759380000,
58+
splitAccessorId: "Men's Shoes",
59+
yAccessorId: 1,
60+
},
61+
{
62+
xAccessorId: 1585759380000,
63+
splitAccessorId: "Women's Clothing",
64+
yAccessorId: 1,
65+
},
66+
{
67+
xAccessorId: 1585760700000,
68+
splitAccessorId: "Men's Clothing",
69+
yAccessorId: 1,
70+
},
71+
{
72+
xAccessorId: 1585760760000,
73+
splitAccessorId: "Men's Clothing",
74+
yAccessorId: 1,
75+
},
76+
{
77+
xAccessorId: 1585760760000,
78+
splitAccessorId: "Men's Shoes",
79+
yAccessorId: 1,
80+
},
81+
{
82+
xAccessorId: 1585761120000,
83+
splitAccessorId: "Men's Shoes",
84+
yAccessorId: 1,
85+
},
86+
],
87+
columns: [
88+
{
89+
id: 'xAccessorId',
90+
name: 'order_date per minute',
91+
meta: {
92+
type: 'date_histogram',
93+
indexPatternId: 'indexPatternId',
94+
aggConfigParams: {
95+
field: 'order_date',
96+
timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' },
97+
useNormalizedEsInterval: true,
98+
scaleMetricValues: false,
99+
interval: '1m',
100+
drop_partials: false,
101+
min_doc_count: 0,
102+
extended_bounds: {},
103+
},
104+
},
105+
formatHint: { id: 'date', params: { pattern: 'HH:mm' } },
106+
},
107+
{
108+
id: 'splitAccessorId',
109+
name: 'Top values of category.keyword',
110+
meta: {
111+
type: 'terms',
112+
indexPatternId: 'indexPatternId',
113+
aggConfigParams: {
114+
field: 'category.keyword',
115+
orderBy: 'yAccessorId',
116+
order: 'desc',
117+
size: 3,
118+
otherBucket: false,
119+
otherBucketLabel: 'Other',
120+
missingBucket: false,
121+
missingBucketLabel: 'Missing',
122+
},
123+
},
124+
formatHint: {
125+
id: 'terms',
126+
params: {
127+
id: 'string',
128+
otherBucketLabel: 'Other',
129+
missingBucketLabel: 'Missing',
130+
parsedUrl: {
131+
origin: 'http://localhost:5601',
132+
pathname: '/jiy/app/kibana',
133+
basePath: '/jiy',
134+
},
135+
},
136+
},
137+
},
138+
{
139+
id: 'yAccessorId',
140+
name: 'Count of records',
141+
meta: {
142+
type: 'count',
143+
indexPatternId: 'indexPatternId',
144+
aggConfigParams: {},
145+
},
146+
formatHint: { id: 'number' },
147+
},
148+
],
149+
},
150+
},
151+
dateRange: {
152+
fromDate: new Date('2020-04-01T16:14:16.246Z'),
153+
toDate: new Date('2020-04-01T17:15:41.263Z'),
154+
},
155+
};
156+
157+
const dateHistogramLayer: LayerArgs = {
158+
layerId: 'timeLayer',
159+
hide: false,
160+
xAccessor: 'xAccessorId',
161+
yScaleType: 'linear',
162+
xScaleType: 'time',
163+
isHistogram: true,
164+
splitAccessor: 'splitAccessorId',
165+
seriesType: 'bar_stacked',
166+
accessors: ['yAccessorId'],
167+
};
168+
30169
const createSampleDatatableWithRows = (rows: KibanaDatatableRow[]): KibanaDatatable => ({
31170
type: 'kibana_datatable',
32171
columns: [
@@ -284,7 +423,7 @@ describe('xy_expression', () => {
284423
Object {
285424
"max": 1546491600000,
286425
"min": 1546405200000,
287-
"minInterval": 1728000,
426+
"minInterval": undefined,
288427
}
289428
`);
290429
});
@@ -449,6 +588,39 @@ describe('xy_expression', () => {
449588
expect(component.find(Settings).prop('rotation')).toEqual(90);
450589
});
451590

591+
test('onBrushEnd returns correct context data for date histogram data', () => {
592+
const { args } = sampleArgs();
593+
594+
const wrapper = mountWithIntl(
595+
<XYChart
596+
data={dateHistogramData}
597+
args={{
598+
...args,
599+
layers: [dateHistogramLayer],
600+
}}
601+
formatFactory={getFormatSpy}
602+
timeZone="UTC"
603+
chartTheme={{}}
604+
histogramBarTarget={50}
605+
executeTriggerActions={executeTriggerActions}
606+
/>
607+
);
608+
609+
wrapper
610+
.find(Settings)
611+
.first()
612+
.prop('onBrushEnd')!(1585757732783, 1585758880838);
613+
614+
expect(executeTriggerActions).toHaveBeenCalledWith('SELECT_RANGE_TRIGGER', {
615+
data: {
616+
column: 0,
617+
table: dateHistogramData.tables.timeLayer,
618+
range: [1585757732783, 1585758880838],
619+
},
620+
timeFieldName: 'order_date',
621+
});
622+
});
623+
452624
test('onElementClick returns correct context data', () => {
453625
const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null };
454626
const series = {

0 commit comments

Comments
 (0)