From 59d83e6955435368a26e50b541edccb1c632d086 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Wed, 7 Oct 2020 19:41:37 +0300 Subject: [PATCH] Add support for HDR percentiles in TSVB visualizations (#78306) * Add support for HDR percentiles in TSVB visualizations Closes: #64238 * remove extra console.log * fix CI * fix PR comments * fix layout * remove legacy injectI18n * fix localization issues Co-authored-by: Elastic Machine Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../vis_type_timeseries/common/vis_schema.ts | 1 + .../application/components/aggs/percentile.js | 32 ++- .../components/aggs/percentile_hdr.tsx | 52 +++++ .../aggs/percentile_rank/multi_value_row.tsx | 31 +-- .../aggs/percentile_rank/percentile_rank.tsx | 46 ++-- .../percentile_rank_values.tsx | 23 +- .../components/aggs/percentile_ui.js | 202 ++++++++++-------- .../server/lib/vis_data/get_series_data.js | 1 - .../lib/vis_data/helpers/bucket_transform.js | 16 +- .../series/percentile_rank.js | 4 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 12 files changed, 254 insertions(+), 156 deletions(-) create mode 100644 src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx diff --git a/src/plugins/vis_type_timeseries/common/vis_schema.ts b/src/plugins/vis_type_timeseries/common/vis_schema.ts index b33215934c5df..0950938423134 100644 --- a/src/plugins/vis_type_timeseries/common/vis_schema.ts +++ b/src/plugins/vis_type_timeseries/common/vis_schema.ts @@ -104,6 +104,7 @@ export const metricsItems = schema.object({ }) ) ), + numberOfSignificantValueDigits: numberOptional, percentiles: schema.maybe( schema.arrayOf( schema.object({ diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js index f12c0c8f6f465..a34b74d106492 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile.js @@ -24,10 +24,11 @@ import { FieldSelect } from './field_select'; import { AggRow } from './agg_row'; import { createChangeHandler } from '../lib/create_change_handler'; import { createSelectHandler } from '../lib/create_select_handler'; +import { createNumberHandler } from '../lib/create_number_handler'; import { htmlIdGenerator, EuiSpacer, - EuiFlexGroup, + EuiFlexGrid, EuiFlexItem, EuiFormLabel, EuiFormRow, @@ -35,6 +36,7 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; import { Percentiles, newPercentile } from './percentile_ui'; +import { PercentileHdr } from './percentile_hdr'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM]; @@ -46,6 +48,8 @@ export function PercentileAgg(props) { const handleChange = createChangeHandler(props.onChange, model); const handleSelectChange = createSelectHandler(handleChange); + const handleNumberChange = createNumberHandler(handleChange); + const indexPattern = (series.override_index_pattern && series.series_index_pattern) || panel.index_pattern; @@ -66,7 +70,7 @@ export function PercentileAgg(props) { siblings={props.siblings} dragHandleProps={props.dragHandleProps} > - + - - - - - + + + } + > + + + + + + + ); } diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx new file mode 100644 index 0000000000000..c0ba7d3c6765b --- /dev/null +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_hdr.tsx @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { EuiFieldNumber, EuiFormRow, EuiIconTip } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; + +export interface PercentileHdrProps { + value: number | undefined; + onChange: () => void; +} + +export const PercentileHdr = ({ value, onChange }: PercentileHdrProps) => ( + + {' '} + + } + type="questionInCircle" + /> + + } + > + + +); diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx index ef8876a19b1a6..444e11d0d563d 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/multi_value_row.tsx @@ -18,15 +18,8 @@ */ import React, { ChangeEvent } from 'react'; import { get } from 'lodash'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - htmlIdGenerator, - EuiFieldNumber, - EuiFlexGroup, - EuiFlexItem, - EuiFormLabel, - EuiSpacer, -} from '@elastic/eui'; + +import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; import { AddDeleteButtons } from '../../add_delete_buttons'; @@ -50,8 +43,6 @@ export const MultiValueRow = ({ disableAdd, disableDelete, }: MultiValueRowProps) => { - const htmlId = htmlIdGenerator(); - const onFieldNumberChange = (event: ChangeEvent) => onChange({ ...model, @@ -59,17 +50,9 @@ export const MultiValueRow = ({ }); return ( -
- - - - - - - + + + onDelete(model)} disableDelete={disableDelete} disableAdd={disableAdd} + responsive={false} /> - -
+ ); }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx index d02a16ade2bba..f78df9b1ddef4 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank.tsx @@ -20,11 +20,11 @@ import React from 'react'; import { htmlIdGenerator, - EuiFlexGroup, EuiFlexItem, EuiFormLabel, EuiFormRow, EuiSpacer, + EuiFlexGrid, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AggSelect } from '../agg_select'; @@ -34,12 +34,16 @@ import { FieldSelect } from '../field_select'; import { createChangeHandler } from '../../lib/create_change_handler'; // @ts-ignore import { createSelectHandler } from '../../lib/create_select_handler'; +// @ts-ignore +import { createNumberHandler } from '../../lib/create_number_handler'; + import { AggRow } from '../agg_row'; import { PercentileRankValues } from './percentile_rank_values'; import { IFieldType, KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; import { MetricsItemsSchema, PanelSchema, SeriesItemsSchema } from '../../../../../common/types'; import { DragHandleProps } from '../../../../types'; +import { PercentileHdr } from '../percentile_hdr'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM]; @@ -67,6 +71,7 @@ export const PercentileRankAgg = (props: PercentileRankAggProps) => { const isTablePanel = panel.type === 'table'; const handleChange = createChangeHandler(props.onChange, model); const handleSelectChange = createSelectHandler(handleChange); + const handleNumberChange = createNumberHandler(handleChange); const handlePercentileRankValuesChange = (values: MetricsItemsSchema['values']) => { handleChange({ @@ -84,7 +89,7 @@ export const PercentileRankAgg = (props: PercentileRankAggProps) => { siblings={props.siblings} dragHandleProps={props.dragHandleProps} > - + { /> - - - {model.values && ( - - )} + + + + } + > + + + + + + + ); }; diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx index b66d79d67f427..92ca4dfb706ac 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_rank/percentile_rank_values.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { last } from 'lodash'; -import { EuiFlexGroup } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { MultiValueRow } from './multi_value_row'; interface PercentileRankValuesProps { @@ -52,19 +52,20 @@ export const PercentileRankValues = (props: PercentileRankValuesProps) => { disableDeleteRow: boolean; disableAddRow: boolean; }) => ( - + + + ); return ( - + {showOnlyLastRow && renderRow({ rowModel: { diff --git a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js index bd421248a3607..fb556a053df3f 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js +++ b/src/plugins/vis_type_timeseries/public/application/components/aggs/percentile_ui.js @@ -19,6 +19,7 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import { i18n } from '@kbn/i18n'; import _ from 'lodash'; import { collectionActions } from '../lib/collection_actions'; import { AddDeleteButtons } from '../add_delete_buttons'; @@ -27,17 +28,19 @@ import { htmlIdGenerator, EuiFlexGroup, EuiFlexItem, - EuiFormLabel, EuiComboBox, EuiFieldNumber, + EuiFormRow, + EuiFlexGrid, + EuiPanel, } from '@elastic/eui'; -import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage } from '@kbn/i18n/react'; export const newPercentile = (opts) => { return _.assign({ id: uuid.v1(), mode: 'line', shade: 0.2 }, opts); }; -class PercentilesUi extends Component { +export class Percentiles extends Component { handleTextChange(item, name) { return (e) => { const handleChange = collectionActions.handleChange.bind(null, this.props); @@ -50,22 +53,31 @@ class PercentilesUi extends Component { renderRow = (row, i, items) => { const defaults = { value: '', percentile: '', shade: '' }; const model = { ...defaults, ...row }; - const { intl, panel } = this.props; + const { panel } = this.props; + const flexItemStyle = { minWidth: 100 }; const percentileFieldNumber = ( - - + + + } + > + + ); @@ -77,99 +89,103 @@ class PercentilesUi extends Component { const handleDelete = collectionActions.handleDelete.bind(null, this.props, model); const modeOptions = [ { - label: intl.formatMessage({ - id: 'visTypeTimeseries.percentile.modeOptions.lineLabel', + label: i18n.translate('visTypeTimeseries.percentile.modeOptions.lineLabel', { defaultMessage: 'Line', }), value: 'line', }, { - label: intl.formatMessage({ - id: 'visTypeTimeseries.percentile.modeOptions.bandLabel', + label: i18n.translate('visTypeTimeseries.percentile.modeOptions.bandLabel', { defaultMessage: 'Band', }), value: 'band', }, ]; - const optionsStyle = {}; + const optionsStyle = { + ...flexItemStyle, + }; if (model.mode === 'line') { optionsStyle.display = 'none'; } - const labelStyle = { marginBottom: 0 }; + const htmlId = htmlIdGenerator(model.id); const selectedModeOption = modeOptions.find((option) => { return model.mode === option.value; }); return ( - - {percentileFieldNumber} - - - - - - - - - - - - - + + + + {percentileFieldNumber} + + + } + > + + + + + + } + > + + + + + + } + > + + + + + + + - - - - - - - - - - - - - - - - - - - + + + ); }; @@ -192,15 +208,13 @@ class PercentilesUi extends Component { } } -PercentilesUi.defaultProps = { +Percentiles.defaultProps = { name: 'percentile', }; -PercentilesUi.propTypes = { +Percentiles.propTypes = { name: PropTypes.string, model: PropTypes.object, panel: PropTypes.object, onChange: PropTypes.func, }; - -export const Percentiles = injectI18n(PercentilesUi); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js index 1eace13c2e336..232efc7514a5a 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/get_series_data.js @@ -43,7 +43,6 @@ export async function getSeriesData(req, panel) { (acc, items) => acc.concat(items), [] ); - const data = await searchStrategy.search(req, searches); const handleResponseBodyFn = handleResponseBody(panel); diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js index f033a43806312..dc2936072165e 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/bucket_transform.js @@ -64,6 +64,16 @@ function extendStatsBucket(bucket, metrics) { return body; } +function getPercentileHdrParam(bucket) { + return bucket.numberOfSignificantValueDigits + ? { + hdr: { + number_of_significant_value_digits: bucket.numberOfSignificantValueDigits, + }, + } + : undefined; +} + export const bucketTransform = { count: () => { return { @@ -139,13 +149,14 @@ export const bucketTransform = { bucket.percentiles.filter((p) => p.percentile).map((p) => p.percentile) ); } - const agg = { + + return { percentiles: { field: bucket.field, percents, + ...getPercentileHdrParam(bucket), }, }; - return agg; }, percentile_rank: (bucket) => { @@ -155,6 +166,7 @@ export const bucketTransform = { percentile_ranks: { field: bucket.field, values: (bucket.values || []).map((value) => (isEmpty(value) ? 0 : value)), + ...getPercentileHdrParam(bucket), }, }; }, diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js index a11324f73e611..c163605af7ac5 100644 --- a/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js +++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/response_processors/series/percentile_rank.js @@ -32,7 +32,7 @@ export function percentileRank(resp, panel, series, meta) { } getSplits(resp, panel, series, meta).forEach((split) => { - (metric.values || []).forEach((percentileRank) => { + (metric.values || []).forEach((percentileRank, index) => { const data = split.timeseries.buckets.map((bucket) => [ bucket.key, getAggValue(bucket, { @@ -43,7 +43,7 @@ export function percentileRank(resp, panel, series, meta) { results.push({ data, - id: `${split.id}:${percentileRank}`, + id: `${split.id}:${percentileRank}:${index}`, label: `${split.label} (${percentileRank || 0})`, color: split.color, ...getDefaultDecoration(series), diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f9a0463ff5803..edae2d16dde95 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3817,7 +3817,6 @@ "visTypeTimeseries.movingAverage.period": "期間", "visTypeTimeseries.movingAverage.windowSizeHint": "ウィンドウは、必ず、期間のサイズの 2 倍以上でなければなりません", "visTypeTimeseries.movingAverage.windowSizeLabel": "ウィンドウサイズ", - "visTypeTimeseries.multivalueRow.valueLabel": "値:", "visTypeTimeseries.noButtonLabel": "いいえ", "visTypeTimeseries.noDataDescription": "選択されたメトリックに表示するデータがありません", "visTypeTimeseries.percentile.aggregationLabel": "集約", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6210f565ca3b4..8ea4577619ac6 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3818,7 +3818,6 @@ "visTypeTimeseries.movingAverage.period": "期间", "visTypeTimeseries.movingAverage.windowSizeHint": "窗口必须始终至少是期间大小的两倍", "visTypeTimeseries.movingAverage.windowSizeLabel": "窗口大小", - "visTypeTimeseries.multivalueRow.valueLabel": "值:", "visTypeTimeseries.noButtonLabel": "否", "visTypeTimeseries.noDataDescription": "所选指标没有可显示的数据", "visTypeTimeseries.percentile.aggregationLabel": "聚合",