Skip to content

Commit

Permalink
[Lens] Allow user to drag and select a subset of the timeline in the …
Browse files Browse the repository at this point in the history
…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 <elasticmachine@users.noreply.github.com>
Co-authored-by: Wylie Conlon <wylieconlon@gmail.com>
  • Loading branch information
3 people authored Apr 30, 2020
1 parent b93427b commit 5887c97
Show file tree
Hide file tree
Showing 7 changed files with 253 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
public supportedTriggers() {
switch (this.savedVis.visualizationType) {
case 'lnsXY':
// TODO: case 'lnsDatatable':
return [VIS_EVENT_TO_TRIGGER.filter];

return [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush];
case 'lnsMetric':
default:
return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { createGetterSetter } from '../../../../../src/plugins/kibana_utils/public';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { createGetterSetter } from '../../../../src/plugins/kibana_utils/public';
import { UiActionsStart } from '../../../../src/plugins/ui_actions/public';

export const [getExecuteTriggerActions, setExecuteTriggerActions] = createGetterSetter<
UiActionsStart['executeTriggerActions']
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion x-pack/plugins/lens/public/xy_visualization/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { xyVisualization } from './xy_visualization';
import { xyChart, getXyChartRenderer } from './xy_expression';
import { legendConfig, xConfig, layerConfig } from './types';
import { EditorFrameSetup, FormatFactory } from '../types';
import { setExecuteTriggerActions } from '../services';
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
import { setExecuteTriggerActions } from './services';

export interface XyVisualizationPluginSetupPlugins {
expressions: ExpressionsSetup;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export const buildExpression = (
.concat(layer.splitAccessor ? [layer.splitAccessor] : [])
.forEach(accessor => {
const operation = datasource.getOperationForColumnId(accessor);
if (operation && operation.label) {
if (operation?.label) {
columnToLabel[accessor] = operation.label;
}
});
Expand Down
174 changes: 173 additions & 1 deletion x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,145 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers';

const executeTriggerActions = jest.fn();

const dateHistogramData: LensMultiTable = {
type: 'lens_multitable',
tables: {
timeLayer: {
type: 'kibana_datatable',
rows: [
{
xAccessorId: 1585758120000,
splitAccessorId: "Men's Clothing",
yAccessorId: 1,
},
{
xAccessorId: 1585758360000,
splitAccessorId: "Women's Accessories",
yAccessorId: 1,
},
{
xAccessorId: 1585758360000,
splitAccessorId: "Women's Clothing",
yAccessorId: 1,
},
{
xAccessorId: 1585759380000,
splitAccessorId: "Men's Clothing",
yAccessorId: 1,
},
{
xAccessorId: 1585759380000,
splitAccessorId: "Men's Shoes",
yAccessorId: 1,
},
{
xAccessorId: 1585759380000,
splitAccessorId: "Women's Clothing",
yAccessorId: 1,
},
{
xAccessorId: 1585760700000,
splitAccessorId: "Men's Clothing",
yAccessorId: 1,
},
{
xAccessorId: 1585760760000,
splitAccessorId: "Men's Clothing",
yAccessorId: 1,
},
{
xAccessorId: 1585760760000,
splitAccessorId: "Men's Shoes",
yAccessorId: 1,
},
{
xAccessorId: 1585761120000,
splitAccessorId: "Men's Shoes",
yAccessorId: 1,
},
],
columns: [
{
id: 'xAccessorId',
name: 'order_date per minute',
meta: {
type: 'date_histogram',
indexPatternId: 'indexPatternId',
aggConfigParams: {
field: 'order_date',
timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' },
useNormalizedEsInterval: true,
scaleMetricValues: false,
interval: '1m',
drop_partials: false,
min_doc_count: 0,
extended_bounds: {},
},
},
formatHint: { id: 'date', params: { pattern: 'HH:mm' } },
},
{
id: 'splitAccessorId',
name: 'Top values of category.keyword',
meta: {
type: 'terms',
indexPatternId: 'indexPatternId',
aggConfigParams: {
field: 'category.keyword',
orderBy: 'yAccessorId',
order: 'desc',
size: 3,
otherBucket: false,
otherBucketLabel: 'Other',
missingBucket: false,
missingBucketLabel: 'Missing',
},
},
formatHint: {
id: 'terms',
params: {
id: 'string',
otherBucketLabel: 'Other',
missingBucketLabel: 'Missing',
parsedUrl: {
origin: 'http://localhost:5601',
pathname: '/jiy/app/kibana',
basePath: '/jiy',
},
},
},
},
{
id: 'yAccessorId',
name: 'Count of records',
meta: {
type: 'count',
indexPatternId: 'indexPatternId',
aggConfigParams: {},
},
formatHint: { id: 'number' },
},
],
},
},
dateRange: {
fromDate: new Date('2020-04-01T16:14:16.246Z'),
toDate: new Date('2020-04-01T17:15:41.263Z'),
},
};

const dateHistogramLayer: LayerArgs = {
layerId: 'timeLayer',
hide: false,
xAccessor: 'xAccessorId',
yScaleType: 'linear',
xScaleType: 'time',
isHistogram: true,
splitAccessor: 'splitAccessorId',
seriesType: 'bar_stacked',
accessors: ['yAccessorId'],
};

const createSampleDatatableWithRows = (rows: KibanaDatatableRow[]): KibanaDatatable => ({
type: 'kibana_datatable',
columns: [
Expand Down Expand Up @@ -284,7 +423,7 @@ describe('xy_expression', () => {
Object {
"max": 1546491600000,
"min": 1546405200000,
"minInterval": 1728000,
"minInterval": undefined,
}
`);
});
Expand Down Expand Up @@ -449,6 +588,39 @@ describe('xy_expression', () => {
expect(component.find(Settings).prop('rotation')).toEqual(90);
});

test('onBrushEnd returns correct context data for date histogram data', () => {
const { args } = sampleArgs();

const wrapper = mountWithIntl(
<XYChart
data={dateHistogramData}
args={{
...args,
layers: [dateHistogramLayer],
}}
formatFactory={getFormatSpy}
timeZone="UTC"
chartTheme={{}}
histogramBarTarget={50}
executeTriggerActions={executeTriggerActions}
/>
);

wrapper
.find(Settings)
.first()
.prop('onBrushEnd')!(1585757732783, 1585758880838);

expect(executeTriggerActions).toHaveBeenCalledWith('SELECT_RANGE_TRIGGER', {
data: {
column: 0,
table: dateHistogramData.tables.timeLayer,
range: [1585757732783, 1585758880838],
},
timeFieldName: 'order_date',
});
});

test('onElementClick returns correct context data', () => {
const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null };
const series = {
Expand Down
Loading

0 comments on commit 5887c97

Please sign in to comment.