Skip to content

Commit

Permalink
Replace flot with elastic-chart in Timelion (#81565)
Browse files Browse the repository at this point in the history
* First draft migrate timelion to elastic-charts

* Some refactoring. Added brush event.

* Added title. Some refactoring

* Fixed some type problems. Added logic for yaxes function

* Fixed some types, added missing functionality for yaxes

* Fixed some types, added missing functionality for stack property

* Fixed unit test

* Removed unneeded code

* Some refactoring

* Some refactoring

* Fixed some remarks.

* Fixed some styles

* Added themes. Removed unneeded styles in BarSeries

* removed unneeded code.

* Fixed some comments

* Fixed vertical cursor across Timelion visualizations of a dashboad

* Fix some problems with styles

* Use RxJS instead of jQuery

* Remove unneeded code

* Fixed some problems

* Fixed unit test

* Fix CI

* Fix eslint

* Fix some gaps

* Fix legend columns

* Some fixes

* add 2 versions of Timeline app

* fix CI

* cleanup code

* fix CI

* fix legend position

* fix some cases

* fix some cases

* remove extra casting

* cleanup code

* fix issue with static

* fix header formatter

* fix points

* fix ts error

* Fix yaxis behavior

* Fix some case with yaxis

* Add deprecation message and update asciidoc

* Fix title

* some text improvements

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Alexey Antonov <alexwizp@gmail.com>
  • Loading branch information
3 people committed Aug 2, 2021
1 parent fb8b162 commit ec85ed6
Show file tree
Hide file tree
Showing 35 changed files with 1,717 additions and 770 deletions.
4 changes: 4 additions & 0 deletions docs/management/advanced-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@ Shows the Timelion tutorial to users when they first open the Timelion app.
Used for calculating automatic intervals in visualizations, this is the number
of buckets to try to represent.

[[timelion-legacyChartsLibrary]]`timelion:legacyChartsLibrary`::
Enables the legacy charts library for timelion charts in Visualize.


[float]
[[kibana-visualization-settings]]
==== Visualization
Expand Down
1 change: 1 addition & 0 deletions src/core/public/doc_links/doc_links_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ export class DocLinksService {
kibanaGeneralSettings: `${KIBANA_DOCS}advanced-options.html#kibana-general-settings`,
kibanaSearchSettings: `${KIBANA_DOCS}advanced-options.html#kibana-search-settings`,
visualizationSettings: `${KIBANA_DOCS}advanced-options.html#kibana-visualization-settings`,
timelionSettings: `${KIBANA_DOCS}advanced-options.html#kibana-timelion-settings`,
},
ml: {
guide: `${ELASTIC_WEBSITE_URL}guide/en/machine-learning/${DOC_LINK_VERSION}/index.html`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
type: 'text',
_meta: { description: 'Non-default value of setting.' },
},
'timelion:legacyChartsLibrary': {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
},
'timelion:target_buckets': {
type: 'long',
_meta: { description: 'Non-default value of setting.' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface UsageStats {
'visualization:tileMap:maxPrecision': number;
'csv:separator': string;
'visualization:tileMap:WMSdefaults': string;
'timelion:legacyChartsLibrary': boolean;
'timelion:target_buckets': number;
'timelion:max_buckets': number;
'timelion:es.timefield': string;
Expand Down
6 changes: 6 additions & 0 deletions src/plugins/telemetry/schema/oss_plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -7264,6 +7264,12 @@
"description": "Non-default value of setting."
}
},
"timelion:legacyChartsLibrary": {
"type": "boolean",
"_meta": {
"description": "Non-default value of setting."
}
},
"timelion:target_buckets": {
"type": "long",
"_meta": {
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/timelion/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
registerListenEventListener,
watchMultiDecorator,
} from '../../kibana_legacy/public';
import { getTimezone } from '../../vis_type_timelion/public';
import { _LEGACY_ as visTypeTimelion } from '../../vis_type_timelion/public';
import { initCellsDirective } from './directives/cells/cells';
import { initFullscreenDirective } from './directives/fullscreen/fullscreen';
import { initFixedElementDirective } from './directives/fixed_element';
Expand Down Expand Up @@ -144,7 +144,7 @@ export function initTimelionApp(app, deps) {
$scope.updatedSheets = [];

const savedVisualizations = deps.plugins.visualizations.savedVisualizationsLoader;
const timezone = getTimezone(deps.core.uiSettings);
const timezone = visTypeTimelion.getTimezone(deps.core.uiSettings);

const defaultExpression = '.es(*)';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import _ from 'lodash';
import { parseTimelionExpressionAsync } from '../../../vis_type_timelion/public';
import { _LEGACY_ as visTypeTimelion } from '../../../vis_type_timelion/public';

export const SUGGESTION_TYPE = {
ARGUMENTS: 'arguments',
Expand Down Expand Up @@ -180,7 +180,7 @@ async function extractSuggestionsFromParsedResult(

export async function suggest(expression, functionList, cursorPosition, argValueSuggestions) {
try {
const result = await parseTimelionExpressionAsync(expression);
const result = await visTypeTimelion.parseTimelionExpressionAsync(expression);
return await extractSuggestionsFromParsedResult(
result,
cursorPosition,
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/timelion/public/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
// styles for timelion visualization are lazy loaded only while a vis is opened
// this will duplicate styles only if both Timelion app and timelion visualization are loaded
// could be left here as it is since the Timelion app is deprecated
@import '../../vis_type_timelion/public/components/timelion_vis.scss';
@import '../../vis_type_timelion/public/legacy/timelion_vis.scss';
18 changes: 6 additions & 12 deletions src/plugins/timelion/public/panels/timechart/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,7 @@ import $ from 'jquery';
import moment from 'moment-timezone';
// @ts-ignore
import observeResize from '../../lib/observe_resize';
import {
calculateInterval,
DEFAULT_TIME_FORMAT,
tickFormatters,
xaxisFormatterProvider,
generateTicksProvider,
} from '../../../../vis_type_timelion/public';
import { _LEGACY_ as visTypeTimelion } from '../../../../vis_type_timelion/public';
import { TimelionVisualizationDependencies } from '../../application';

const DEBOUNCE_DELAY = 50;
Expand All @@ -37,9 +31,9 @@ export function timechartFn(dependencies: TimelionVisualizationDependencies) {
help: 'Draw a timeseries chart',
render($scope: any, $elem: any) {
const template = '<div class="chart-top-title"></div><div class="chart-canvas"></div>';
const formatters = tickFormatters() as any;
const getxAxisFormatter = xaxisFormatterProvider(uiSettings);
const generateTicks = generateTicksProvider();
const formatters = visTypeTimelion.tickFormatters() as any;
const getxAxisFormatter = visTypeTimelion.xaxisFormatterProvider(uiSettings);
const generateTicks = visTypeTimelion.generateTicksProvider();

// TODO: I wonder if we should supply our own moment that sets this every time?
// could just use angular's injection to provide a moment service?
Expand Down Expand Up @@ -226,7 +220,7 @@ export function timechartFn(dependencies: TimelionVisualizationDependencies) {
if (legendCaption) {
legendCaption.text(
moment(pos.x).format(
_.get(dataset, '[0]._global.legend.timeFormat', DEFAULT_TIME_FORMAT)
_.get(dataset, '[0]._global.legend.timeFormat', visTypeTimelion.DEFAULT_TIME_FORMAT)
)
);
}
Expand Down Expand Up @@ -289,7 +283,7 @@ export function timechartFn(dependencies: TimelionVisualizationDependencies) {

// Get the X-axis tick format
const time = timefilter.timefilter.getBounds() as any;
const interval = calculateInterval(
const interval = visTypeTimelion.calculateInterval(
time.min.valueOf(),
time.max.valueOf(),
uiSettings.get('timelion:target_buckets') || 200,
Expand Down
18 changes: 18 additions & 0 deletions src/plugins/vis_type_timelion/common/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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 const UI_SETTINGS = {
LEGACY_CHARTS_LIBRARY: 'timelion:legacyChartsLibrary',
ES_TIMEFIELD: 'timelion:es.timefield',
DEFAULT_INDEX: 'timelion:es.default_index',
TARGET_BUCKETS: 'timelion:target_buckets',
MAX_BUCKETS: 'timelion:max_buckets',
MIN_INTERVAL: 'timelion:min_interval',
GRAPHITE_URL: 'timelion:graphite.url',
QUANDL_KEY: 'timelion:quandl.key',
};
33 changes: 33 additions & 0 deletions src/plugins/vis_type_timelion/common/vis_data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 interface VisSeries {
yaxis?: number;
label: string;
lines?: {
show?: boolean;
lineWidth?: number;
fill?: number;
steps?: number;
};
points?: {
show?: boolean;
symbol?: 'cross' | 'x' | 'circle' | 'square' | 'diamond' | 'plus' | 'triangle';
fillColor?: string;
fill?: number;
radius?: number;
lineWidth?: number;
};
bars: {
lineWidth?: number;
fill?: number;
};
color?: string;
data: Array<Array<number | null>>;
stack: boolean;
}
2 changes: 1 addition & 1 deletion src/plugins/vis_type_timelion/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"kibanaVersion": "kibana",
"server": true,
"ui": true,
"requiredPlugins": ["visualizations", "data", "expressions"],
"requiredPlugins": ["visualizations", "data", "expressions", "charts"],
"requiredBundles": ["kibanaUtils", "kibanaReact", "visDefaultEditor"],
"owner": {
"name": "Kibana App",
Expand Down
62 changes: 62 additions & 0 deletions src/plugins/vis_type_timelion/public/components/series/area.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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 React from 'react';
import { AreaSeries, ScaleType, CurveType, AreaSeriesStyle, PointShape } from '@elastic/charts';
import type { VisSeries } from '../../../common/vis_data';

interface AreaSeriesComponentProps {
index: number;
visData: VisSeries;
groupId: string;
}

const isShowLines = (lines: VisSeries['lines'], points: VisSeries['points']) =>
lines?.show ? true : points?.show ? false : true;

const getAreaSeriesStyle = ({ color, lines, points }: AreaSeriesComponentProps['visData']) =>
({
line: {
opacity: isShowLines(lines, points) ? 1 : 0,
stroke: color,
strokeWidth: lines?.lineWidth !== undefined ? Number(lines.lineWidth) : 3,
visible: isShowLines(lines, points),
},
area: {
fill: color,
opacity: lines?.fill ?? 0,
visible: lines?.show ?? points?.show ?? true,
},
point: {
fill: points?.fillColor ?? color,
opacity: points?.lineWidth !== undefined ? (points.fill || 1) * 10 : 10,
radius: points?.radius ?? 3,
stroke: color,
strokeWidth: points?.lineWidth ?? 2,
visible: points?.show ?? false,
shape: points?.symbol === 'cross' ? PointShape.X : points?.symbol,
},
curve: lines?.steps ? CurveType.CURVE_STEP : CurveType.LINEAR,
} as AreaSeriesStyle);

export const AreaSeriesComponent = ({ index, groupId, visData }: AreaSeriesComponentProps) => (
<AreaSeries
id={index + visData.label}
groupId={groupId}
name={visData.label}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor={0}
yAccessors={[1]}
data={visData.data}
sortIndex={index}
color={visData.color}
stackAccessors={visData.stack ? [0] : undefined}
areaSeriesStyle={getAreaSeriesStyle(visData)}
/>
);
58 changes: 58 additions & 0 deletions src/plugins/vis_type_timelion/public/components/series/bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* 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 React from 'react';
import { BarSeries, ScaleType, BarSeriesStyle } from '@elastic/charts';
import type { VisSeries } from '../../../common/vis_data';

interface BarSeriesComponentProps {
index: number;
visData: VisSeries;
groupId: string;
}

const getBarSeriesStyle = ({ color, bars }: BarSeriesComponentProps['visData']) => {
let opacity = bars.fill ?? 1;

if (opacity < 0) {
opacity = 0;
} else if (opacity > 1) {
opacity = 1;
}

return {
rectBorder: {
stroke: color,
strokeWidth: Math.max(1, bars.lineWidth ? Math.ceil(bars.lineWidth / 2) : 1),
visible: true,
},
rect: {
fill: color,
opacity,
widthPixel: 1,
},
} as BarSeriesStyle;
};

export const BarSeriesComponent = ({ index, groupId, visData }: BarSeriesComponentProps) => (
<BarSeries
id={index + visData.label}
groupId={groupId}
name={visData.label}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor={0}
yAccessors={[1]}
data={visData.data}
sortIndex={index}
enableHistogramMode={false}
color={visData.color}
stackAccessors={visData.stack ? [0] : undefined}
barSeriesStyle={getBarSeriesStyle(visData)}
/>
);
10 changes: 10 additions & 0 deletions src/plugins/vis_type_timelion/public/components/series/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* 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 { BarSeriesComponent } from './bar';
export { AreaSeriesComponent } from './area';
56 changes: 3 additions & 53 deletions src/plugins/vis_type_timelion/public/components/timelion_vis.scss
Original file line number Diff line number Diff line change
@@ -1,60 +1,10 @@
.timChart {
.timelionChart {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;

// Custom Jquery FLOT / schema selectors
// Cannot change at the moment

.chart-top-title {
@include euiFontSizeXS;
flex: 0;
text-align: center;
font-weight: $euiFontWeightBold;
}

.chart-canvas {
min-width: 100%;
flex: 1;
overflow: hidden;
}

.legendLabel {
white-space: nowrap;
text-overflow: ellipsis;
overflow-x: hidden;
line-height: normal;
}

.legendColorBox {
vertical-align: middle;
}

.ngLegendValue {
color: $euiTextColor;
cursor: pointer;

&:focus,
&:hover {
text-decoration: underline;
}
}

.ngLegendValueNumber {
margin-left: $euiSizeXS;
margin-right: $euiSizeXS;
font-weight: $euiFontWeightBold;
}

.flot-tick-label {
font-size: $euiFontSizeXS;
color: $euiColorDarkShade;
}
}

.timChart__legendCaption {
color: $euiTextColor;
white-space: nowrap;
font-weight: $euiFontWeightBold;
.timelionChart__topTitle {
text-align: center;
}
Loading

0 comments on commit ec85ed6

Please sign in to comment.