mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Lens] Switch to unified metric renderer (#126019)
* Switch to unified metric renderer * Fix tests * Fix snapshots * Remove unused translation * Fix tests * Fix font size mapping Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
2836018b0e
commit
9f880f207f
35 changed files with 229 additions and 1262 deletions
|
@ -43,6 +43,7 @@ export const MetricVisValue = ({
|
|||
style={autoScale && colorFullBackground ? {} : { backgroundColor: metric.bgColor }}
|
||||
>
|
||||
<div
|
||||
data-test-subj="metric_value"
|
||||
className="mtrVis__value"
|
||||
style={{
|
||||
...(style.spec as CSSProperties),
|
||||
|
@ -60,6 +61,7 @@ export const MetricVisValue = ({
|
|||
/>
|
||||
{labelConfig.show && (
|
||||
<div
|
||||
data-test-subj="metric_label"
|
||||
style={{
|
||||
...(labelConfig.style.spec as CSSProperties),
|
||||
order: labelConfig.position === 'top' ? -1 : 2,
|
||||
|
|
|
@ -33,6 +33,7 @@ export const getMetricVisRenderer = (
|
|||
render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<VisualizationContainer
|
||||
data-test-subj="mtrVis"
|
||||
className="mtrVis"
|
||||
showNoResult={!visData.rows?.length}
|
||||
handlers={handlers}
|
||||
|
|
|
@ -11,7 +11,6 @@ export * from './rename_columns';
|
|||
export * from './merge_tables';
|
||||
export * from './time_scale';
|
||||
export * from './datatable';
|
||||
export * from './metric_chart';
|
||||
export * from './xy_chart';
|
||||
|
||||
export * from './expression_types';
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './types';
|
||||
export * from './metric_chart';
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ColorMode } from '../../../../../../src/plugins/charts/common';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { LensMultiTable } from '../../types';
|
||||
import type { MetricConfig } from './types';
|
||||
|
||||
interface MetricRender {
|
||||
type: 'render';
|
||||
as: 'lens_metric_chart_renderer';
|
||||
value: MetricChartProps;
|
||||
}
|
||||
|
||||
export interface MetricChartProps {
|
||||
data: LensMultiTable;
|
||||
args: MetricConfig;
|
||||
}
|
||||
|
||||
export const metricChart: ExpressionFunctionDefinition<
|
||||
'lens_metric_chart',
|
||||
LensMultiTable,
|
||||
Omit<MetricConfig, 'layerId' | 'layerType'>,
|
||||
MetricRender
|
||||
> = {
|
||||
name: 'lens_metric_chart',
|
||||
type: 'render',
|
||||
help: 'A metric chart',
|
||||
args: {
|
||||
title: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.metric.title.help', {
|
||||
defaultMessage: 'The visualization title.',
|
||||
}),
|
||||
},
|
||||
size: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.metric.size.help', {
|
||||
defaultMessage: 'The visualization text size.',
|
||||
}),
|
||||
default: 'xl',
|
||||
},
|
||||
titlePosition: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.metric.titlePosition.help', {
|
||||
defaultMessage: 'The visualization title position.',
|
||||
}),
|
||||
default: 'bottom',
|
||||
},
|
||||
textAlign: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.metric.textAlignPosition.help', {
|
||||
defaultMessage: 'The visualization text alignment position.',
|
||||
}),
|
||||
default: 'center',
|
||||
},
|
||||
description: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
metricTitle: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.metric.metricTitle.help', {
|
||||
defaultMessage: 'The title of the metric shown.',
|
||||
}),
|
||||
},
|
||||
accessor: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('xpack.lens.metric.accessor.help', {
|
||||
defaultMessage: 'The column whose value is being displayed',
|
||||
}),
|
||||
},
|
||||
mode: {
|
||||
types: ['string'],
|
||||
options: ['reduced', 'full'],
|
||||
default: 'full',
|
||||
help: i18n.translate('xpack.lens.metric.mode.help', {
|
||||
defaultMessage:
|
||||
'The display mode of the chart - reduced will only show the metric itself without min size',
|
||||
}),
|
||||
},
|
||||
colorMode: {
|
||||
types: ['string'],
|
||||
default: `"${ColorMode.None}"`,
|
||||
options: [ColorMode.None, ColorMode.Labels, ColorMode.Background],
|
||||
help: i18n.translate('xpack.lens.metric.colorMode.help', {
|
||||
defaultMessage: 'Which part of metric to color',
|
||||
}),
|
||||
},
|
||||
palette: {
|
||||
types: ['palette'],
|
||||
help: i18n.translate('xpack.lens.metric.palette.help', {
|
||||
defaultMessage: 'Provides colors for the values',
|
||||
}),
|
||||
},
|
||||
},
|
||||
inputTypes: ['lens_multitable'],
|
||||
fn(data, args) {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'lens_metric_chart_renderer',
|
||||
value: {
|
||||
data,
|
||||
args,
|
||||
},
|
||||
} as MetricRender;
|
||||
},
|
||||
};
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
ColorMode,
|
||||
CustomPaletteState,
|
||||
PaletteOutput,
|
||||
} from '../../../../../../src/plugins/charts/common';
|
||||
import { CustomPaletteParams, LayerType } from '../../types';
|
||||
|
||||
export interface MetricState {
|
||||
layerId: string;
|
||||
accessor?: string;
|
||||
layerType: LayerType;
|
||||
colorMode?: ColorMode;
|
||||
palette?: PaletteOutput<CustomPaletteParams>;
|
||||
titlePosition?: 'top' | 'bottom';
|
||||
size?: string;
|
||||
textAlign?: 'left' | 'right' | 'center';
|
||||
}
|
||||
|
||||
export interface MetricConfig extends Omit<MetricState, 'palette' | 'colorMode'> {
|
||||
title: string;
|
||||
description: string;
|
||||
metricTitle: string;
|
||||
mode: 'reduced' | 'full';
|
||||
colorMode: ColorMode;
|
||||
palette: PaletteOutput<CustomPaletteState>;
|
||||
}
|
|
@ -13,8 +13,11 @@ import type {
|
|||
SerializedFieldFormat,
|
||||
} from '../../../../src/plugins/field_formats/common';
|
||||
import type { Datatable } from '../../../../src/plugins/expressions/common';
|
||||
import type { PaletteContinuity } from '../../../../src/plugins/charts/common';
|
||||
import type { PaletteOutput } from '../../../../src/plugins/charts/common';
|
||||
import type {
|
||||
PaletteContinuity,
|
||||
PaletteOutput,
|
||||
ColorMode,
|
||||
} from '../../../../src/plugins/charts/common';
|
||||
import {
|
||||
CategoryDisplay,
|
||||
layerTypes,
|
||||
|
@ -121,3 +124,13 @@ export interface PieVisualizationState {
|
|||
layers: PieLayerState[];
|
||||
palette?: PaletteOutput;
|
||||
}
|
||||
export interface MetricState {
|
||||
layerId: string;
|
||||
accessor?: string;
|
||||
layerType: LayerType;
|
||||
colorMode?: ColorMode;
|
||||
palette?: PaletteOutput<CustomPaletteParams>;
|
||||
titlePosition?: 'top' | 'bottom';
|
||||
size?: string;
|
||||
textAlign?: 'left' | 'right' | 'center';
|
||||
}
|
||||
|
|
|
@ -24,8 +24,7 @@ import type { LensByReferenceInput, LensByValueInput } from './embeddable';
|
|||
import type { Document } from '../persistence';
|
||||
import type { IndexPatternPersistedState } from '../indexpattern_datasource/types';
|
||||
import type { XYState } from '../xy_visualization/types';
|
||||
import type { MetricState } from '../../common/expressions';
|
||||
import type { PieVisualizationState } from '../../common';
|
||||
import type { PieVisualizationState, MetricState } from '../../common';
|
||||
import type { DatatableVisualizationState } from '../datatable_visualization/visualization';
|
||||
import type { HeatmapVisualizationState } from '../heatmap_visualization/types';
|
||||
import type { GaugeVisualizationState } from '../visualizations/gauge/constants';
|
||||
|
|
|
@ -30,7 +30,6 @@ import { renameColumns } from '../common/expressions/rename_columns/rename_colum
|
|||
import { formatColumn } from '../common/expressions/format_column';
|
||||
import { counterRate } from '../common/expressions/counter_rate';
|
||||
import { getTimeScale } from '../common/expressions/time_scale/time_scale';
|
||||
import { metricChart } from '../common/expressions/metric_chart/metric_chart';
|
||||
import { lensMultitable } from '../common/expressions';
|
||||
|
||||
export const setupExpressions = (
|
||||
|
@ -44,7 +43,6 @@ export const setupExpressions = (
|
|||
xyChart,
|
||||
mergeTables,
|
||||
counterRate,
|
||||
metricChart,
|
||||
yAxisConfig,
|
||||
dataLayerConfig,
|
||||
referenceLineLayerConfig,
|
||||
|
|
|
@ -14,7 +14,6 @@ export type {
|
|||
export type { XYState } from './xy_visualization/types';
|
||||
export type { DataType, OperationMetadata, Visualization } from './types';
|
||||
export type {
|
||||
MetricState,
|
||||
AxesSettingsConfig,
|
||||
XYLayerConfig,
|
||||
LegendConfig,
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { computeScale, AutoScale } from './auto_scale';
|
||||
import { render } from 'enzyme';
|
||||
|
||||
const mockElement = (clientWidth = 100, clientHeight = 200) => ({
|
||||
clientHeight,
|
||||
clientWidth,
|
||||
});
|
||||
|
||||
describe('AutoScale', () => {
|
||||
describe('computeScale', () => {
|
||||
it('is 1 if any element is null', () => {
|
||||
expect(computeScale(null, null)).toBe(1);
|
||||
expect(computeScale(mockElement(), null)).toBe(1);
|
||||
expect(computeScale(null, mockElement())).toBe(1);
|
||||
});
|
||||
|
||||
it('is never over 1', () => {
|
||||
expect(computeScale(mockElement(2000, 2000), mockElement(1000, 1000))).toBe(1);
|
||||
});
|
||||
|
||||
it('is never under 0.3 in default case', () => {
|
||||
expect(computeScale(mockElement(2000, 1000), mockElement(1000, 10000))).toBe(0.3);
|
||||
});
|
||||
|
||||
it('is never under specified min scale if specified', () => {
|
||||
expect(computeScale(mockElement(2000, 1000), mockElement(1000, 10000), 0.1)).toBe(0.1);
|
||||
});
|
||||
|
||||
it('is the lesser of the x or y scale', () => {
|
||||
expect(computeScale(mockElement(2000, 2000), mockElement(3000, 5000))).toBe(0.4);
|
||||
expect(computeScale(mockElement(2000, 3000), mockElement(4000, 3200))).toBe(0.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('AutoScale', () => {
|
||||
it('renders', () => {
|
||||
expect(
|
||||
render(
|
||||
<AutoScale>
|
||||
<h1>Hoi!</h1>
|
||||
</AutoScale>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<div
|
||||
style="display:flex;justify-content:center;align-items:center;max-width:100%;max-height:100%;overflow:hidden;line-height:1.5"
|
||||
>
|
||||
<div
|
||||
class="lnsMetricExpression__containerScale"
|
||||
style="transform:scale(0)"
|
||||
>
|
||||
<h1>
|
||||
Hoi!
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { throttle } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { EuiResizeObserver } from '@elastic/eui';
|
||||
import { MetricState } from '../../common/expressions';
|
||||
|
||||
interface Props extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children: React.ReactNode | React.ReactNode[];
|
||||
minScale?: number;
|
||||
size?: MetricState['size'];
|
||||
titlePosition?: MetricState['titlePosition'];
|
||||
textAlign?: MetricState['textAlign'];
|
||||
}
|
||||
|
||||
interface State {
|
||||
scale: number;
|
||||
}
|
||||
|
||||
export class AutoScale extends React.Component<Props, State> {
|
||||
private child: Element | null = null;
|
||||
private parent: Element | null = null;
|
||||
private scale: () => void;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.scale = throttle(() => {
|
||||
const scale = computeScale(this.parent, this.child, this.props.minScale);
|
||||
|
||||
// Prevent an infinite render loop
|
||||
if (this.state.scale !== scale) {
|
||||
this.setState({ scale });
|
||||
}
|
||||
});
|
||||
|
||||
// An initial scale of 0 means we always redraw
|
||||
// at least once, which is sub-optimal, but it
|
||||
// prevents an annoying flicker.
|
||||
this.state = { scale: 0 };
|
||||
}
|
||||
|
||||
setParent = (el: Element | null) => {
|
||||
if (el && this.parent !== el) {
|
||||
this.parent = el;
|
||||
setTimeout(() => this.scale());
|
||||
}
|
||||
};
|
||||
|
||||
setChild = (el: Element | null) => {
|
||||
if (el && this.child !== el) {
|
||||
this.child = el;
|
||||
setTimeout(() => this.scale());
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children, minScale, size, textAlign, titlePosition, ...rest } = this.props;
|
||||
const { scale } = this.state;
|
||||
const style = this.props.style || {};
|
||||
|
||||
return (
|
||||
<EuiResizeObserver onResize={this.scale}>
|
||||
{(resizeRef) => (
|
||||
<div
|
||||
{...rest}
|
||||
ref={(el) => {
|
||||
this.setParent(el);
|
||||
resizeRef(el);
|
||||
}}
|
||||
style={{
|
||||
...style,
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
overflow: 'hidden',
|
||||
lineHeight: 1.5,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={this.setChild}
|
||||
style={{
|
||||
transform: `scale(${scale})`,
|
||||
}}
|
||||
className={classNames('lnsMetricExpression__containerScale', {
|
||||
alignLeft: textAlign === 'left',
|
||||
alignRight: textAlign === 'right',
|
||||
alignCenter: textAlign === 'center',
|
||||
[`titleSize${size?.toUpperCase()}`]: Boolean(size),
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</EuiResizeObserver>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface ClientDimensionable {
|
||||
clientWidth: number;
|
||||
clientHeight: number;
|
||||
}
|
||||
|
||||
const MAX_SCALE = 1;
|
||||
const MIN_SCALE = 0.3;
|
||||
|
||||
/**
|
||||
* computeScale computes the ratio by which the child needs to shrink in order
|
||||
* to fit into the parent. This function is only exported for testing purposes.
|
||||
*/
|
||||
export function computeScale(
|
||||
parent: ClientDimensionable | null,
|
||||
child: ClientDimensionable | null,
|
||||
minScale: number = MIN_SCALE
|
||||
) {
|
||||
if (!parent || !child) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const scaleX = parent.clientWidth / child.clientWidth;
|
||||
const scaleY = parent.clientHeight / child.clientHeight;
|
||||
|
||||
return Math.max(Math.min(MAX_SCALE, Math.min(scaleX, scaleY)), minScale);
|
||||
}
|
|
@ -16,7 +16,7 @@ import { ColorMode, PaletteOutput, PaletteRegistry } from 'src/plugins/charts/pu
|
|||
import { act } from 'react-dom/test-utils';
|
||||
import { CustomizablePalette, PalettePanelContainer } from '../shared_components';
|
||||
import { CustomPaletteParams, layerTypes } from '../../common';
|
||||
import { MetricState } from '../../common/expressions';
|
||||
import type { MetricState } from '../../common/types';
|
||||
|
||||
// mocking random id generator function
|
||||
jest.mock('@elastic/eui', () => {
|
||||
|
|
|
@ -17,7 +17,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import { ColorMode } from '../../../../../src/plugins/charts/common';
|
||||
import type { PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
import { isNumericFieldForDatatable, MetricState } from '../../common/expressions';
|
||||
import type { MetricState } from '../../common/types';
|
||||
import { isNumericFieldForDatatable } from '../../common/expressions';
|
||||
import {
|
||||
applyPaletteParams,
|
||||
CustomizablePalette,
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
.lnsMetricExpression__container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
|
||||
.lnsMetricExpression__value {
|
||||
font-size: $euiFontSizeXXL * 2;
|
||||
font-weight: $euiFontWeightSemiBold;
|
||||
border-radius: $euiBorderRadius;
|
||||
}
|
||||
|
||||
.lnsMetricExpression__title {
|
||||
font-size: $euiFontSizeXXL;
|
||||
color: $euiTextColor;
|
||||
&.reverseOrder {
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.lnsMetricExpression__containerScale {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
&.alignLeft {
|
||||
align-items: start;
|
||||
}
|
||||
&.alignRight {
|
||||
align-items: end;
|
||||
}
|
||||
&.alignCenter {
|
||||
align-items: center;
|
||||
}
|
||||
&.titleSizeXS {
|
||||
.lnsMetricExpression__title {
|
||||
font-size: $euiFontSizeXS;
|
||||
}
|
||||
.lnsMetricExpression__value {
|
||||
font-size: $euiFontSizeXS * 2;
|
||||
}
|
||||
}
|
||||
&.titleSizeS {
|
||||
.lnsMetricExpression__title {
|
||||
font-size: $euiFontSizeS;
|
||||
}
|
||||
.lnsMetricExpression__value {
|
||||
font-size: $euiFontSizeM * 2.25;
|
||||
}
|
||||
}
|
||||
&.titleSizeM {
|
||||
.lnsMetricExpression__title {
|
||||
font-size: $euiFontSizeM;
|
||||
}
|
||||
.lnsMetricExpression__value {
|
||||
font-size: $euiFontSizeL * 2;
|
||||
}
|
||||
}
|
||||
&.titleSizeL {
|
||||
.lnsMetricExpression__title {
|
||||
font-size: $euiFontSizeL;
|
||||
}
|
||||
.lnsMetricExpression__value {
|
||||
font-size: $euiFontSizeXL * 2;
|
||||
}
|
||||
}
|
||||
&.titleSizeXL {
|
||||
.lnsMetricExpression__title {
|
||||
font-size: $euiFontSizeXL;
|
||||
}
|
||||
.lnsMetricExpression__value {
|
||||
font-size: $euiFontSizeXXL * 2;
|
||||
}
|
||||
}
|
||||
|
||||
&.titleSizeXXL {
|
||||
.lnsMetricExpression__title {
|
||||
font-size: $euiFontSizeXXL;
|
||||
}
|
||||
.lnsMetricExpression__value {
|
||||
font-size: $euiFontSizeXXL * 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,552 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { MetricChart } from './expression';
|
||||
import { MetricConfig, metricChart } from '../../common/expressions';
|
||||
import React from 'react';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks';
|
||||
import type { IFieldFormat } from '../../../../../src/plugins/field_formats/common';
|
||||
import { layerTypes } from '../../common';
|
||||
import type { LensMultiTable } from '../../common';
|
||||
import { IUiSettingsClient } from 'kibana/public';
|
||||
import { ColorMode } from 'src/plugins/charts/common';
|
||||
|
||||
function sampleArgs() {
|
||||
const data: LensMultiTable = {
|
||||
type: 'lens_multitable',
|
||||
tables: {
|
||||
l1: {
|
||||
type: 'datatable',
|
||||
columns: [
|
||||
// Simulating a calculated column like a formula
|
||||
{ id: 'a', name: 'a', meta: { type: 'string', params: { id: 'string' } } },
|
||||
{ id: 'b', name: 'b', meta: { type: 'string' } },
|
||||
{
|
||||
id: 'c',
|
||||
name: 'c',
|
||||
meta: { type: 'number', params: { id: 'percent', params: { format: '0.000%' } } },
|
||||
},
|
||||
],
|
||||
rows: [{ a: 'last', b: 'last', c: 3 }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const args: MetricConfig = {
|
||||
accessor: 'c',
|
||||
layerId: 'l1',
|
||||
layerType: layerTypes.DATA,
|
||||
title: 'My fanci metric chart',
|
||||
description: 'Fancy chart description',
|
||||
metricTitle: 'My fanci metric chart',
|
||||
mode: 'full',
|
||||
colorMode: ColorMode.None,
|
||||
palette: { type: 'palette', name: 'status' },
|
||||
};
|
||||
|
||||
const noAttributesArgs: MetricConfig = {
|
||||
accessor: 'c',
|
||||
layerId: 'l1',
|
||||
layerType: layerTypes.DATA,
|
||||
title: '',
|
||||
description: '',
|
||||
metricTitle: 'My fanci metric chart',
|
||||
mode: 'full',
|
||||
colorMode: ColorMode.None,
|
||||
palette: { type: 'palette', name: 'status' },
|
||||
};
|
||||
|
||||
return { data, args, noAttributesArgs };
|
||||
}
|
||||
|
||||
describe('metric_expression', () => {
|
||||
describe('metricChart', () => {
|
||||
test('it renders with the specified data and args', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
const result = metricChart.fn(data, args, createMockExecutionContext());
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'render',
|
||||
as: 'lens_metric_chart_renderer',
|
||||
value: { data, args },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MetricChart component', () => {
|
||||
test('it renders all attributes when passed (title, description, metricTitle, value)', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<VisualizationContainer
|
||||
className="lnsMetricExpression__container"
|
||||
style={Object {}}
|
||||
>
|
||||
<AutoScale
|
||||
key="3"
|
||||
>
|
||||
<div
|
||||
className="lnsMetricExpression__title"
|
||||
data-test-subj="lns_metric_title"
|
||||
>
|
||||
My fanci metric chart
|
||||
</div>
|
||||
<div
|
||||
className="lnsMetricExpression__value"
|
||||
data-test-subj="lns_metric_value"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
</AutoScale>
|
||||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it renders strings', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
args.accessor = 'a';
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<VisualizationContainer
|
||||
className="lnsMetricExpression__container"
|
||||
style={Object {}}
|
||||
>
|
||||
<AutoScale
|
||||
key="last"
|
||||
>
|
||||
<div
|
||||
className="lnsMetricExpression__title"
|
||||
data-test-subj="lns_metric_title"
|
||||
>
|
||||
My fanci metric chart
|
||||
</div>
|
||||
<div
|
||||
className="lnsMetricExpression__value"
|
||||
data-test-subj="lns_metric_value"
|
||||
>
|
||||
last
|
||||
</div>
|
||||
</AutoScale>
|
||||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it renders only chart content when title and description are empty strings', () => {
|
||||
const { data, noAttributesArgs } = sampleArgs();
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={noAttributesArgs}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<VisualizationContainer
|
||||
className="lnsMetricExpression__container"
|
||||
style={Object {}}
|
||||
>
|
||||
<AutoScale
|
||||
key="3"
|
||||
>
|
||||
<div
|
||||
className="lnsMetricExpression__title"
|
||||
data-test-subj="lns_metric_title"
|
||||
>
|
||||
My fanci metric chart
|
||||
</div>
|
||||
<div
|
||||
className="lnsMetricExpression__value"
|
||||
data-test-subj="lns_metric_value"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
</AutoScale>
|
||||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it does not render metricTitle in reduced mode', () => {
|
||||
const { data, noAttributesArgs } = sampleArgs();
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={{ ...noAttributesArgs, mode: 'reduced' }}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<VisualizationContainer
|
||||
className="lnsMetricExpression__container"
|
||||
style={Object {}}
|
||||
>
|
||||
<AutoScale
|
||||
key="3"
|
||||
minScale={0.05}
|
||||
>
|
||||
<div
|
||||
className="lnsMetricExpression__value"
|
||||
data-test-subj="lns_metric_value"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
</AutoScale>
|
||||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it renders an EmptyPlaceholder when no tables is passed as data', () => {
|
||||
const { data, noAttributesArgs } = sampleArgs();
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={{ ...data, tables: {} }}
|
||||
args={noAttributesArgs}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<VisualizationContainer
|
||||
className="lnsMetricExpression__container"
|
||||
>
|
||||
<EmptyPlaceholder
|
||||
icon={[Function]}
|
||||
/>
|
||||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it renders an EmptyPlaceholder when null value is passed as data', () => {
|
||||
const { data, noAttributesArgs } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].c = null;
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={noAttributesArgs}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<VisualizationContainer
|
||||
className="lnsMetricExpression__container"
|
||||
>
|
||||
<EmptyPlaceholder
|
||||
icon={[Function]}
|
||||
/>
|
||||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it renders 0 value', () => {
|
||||
const { data, noAttributesArgs } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].c = 0;
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={noAttributesArgs}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<VisualizationContainer
|
||||
className="lnsMetricExpression__container"
|
||||
style={Object {}}
|
||||
>
|
||||
<AutoScale
|
||||
key="0"
|
||||
>
|
||||
<div
|
||||
className="lnsMetricExpression__title"
|
||||
data-test-subj="lns_metric_title"
|
||||
>
|
||||
My fanci metric chart
|
||||
</div>
|
||||
<div
|
||||
className="lnsMetricExpression__value"
|
||||
data-test-subj="lns_metric_value"
|
||||
>
|
||||
0
|
||||
</div>
|
||||
</AutoScale>
|
||||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it finds the right column to format', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
const factory = jest.fn(() => ({ convert: (x) => x } as IFieldFormat));
|
||||
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={factory}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
);
|
||||
expect(factory).toHaveBeenCalledWith({ id: 'percent', params: { format: '0.000%' } });
|
||||
});
|
||||
|
||||
test('it renders the correct color styling for numeric value if coloring config is passed', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
|
||||
args.colorMode = ColorMode.Labels;
|
||||
args.palette.params = {
|
||||
rangeMin: 0,
|
||||
rangeMax: 400,
|
||||
stops: [100, 200, 400],
|
||||
gradient: false,
|
||||
range: 'number',
|
||||
colors: ['red', 'yellow', 'green'],
|
||||
};
|
||||
|
||||
const instance = mount(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style')
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
color: 'red',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders no color styling for numeric value if value is lower then rangeMin and continuity is "above"', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].c = -1;
|
||||
args.colorMode = ColorMode.Labels;
|
||||
args.palette.params = {
|
||||
rangeMin: 0,
|
||||
rangeMax: 400,
|
||||
stops: [100, 200, 400],
|
||||
gradient: false,
|
||||
range: 'number',
|
||||
colors: ['red', 'yellow', 'green'],
|
||||
continuity: 'above',
|
||||
};
|
||||
|
||||
const instance = mount(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style')
|
||||
).not.toEqual(
|
||||
expect.objectContaining({
|
||||
color: expect.any(String),
|
||||
})
|
||||
);
|
||||
});
|
||||
test('it renders no color styling for numeric value if value is higher than rangeMax and continuity is "below"', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].c = 500;
|
||||
args.colorMode = ColorMode.Labels;
|
||||
args.palette.params = {
|
||||
rangeMin: 0,
|
||||
rangeMax: 400,
|
||||
stops: [100, 200, 400],
|
||||
gradient: false,
|
||||
range: 'number',
|
||||
colors: ['red', 'yellow', 'green'],
|
||||
continuity: 'below',
|
||||
};
|
||||
|
||||
const instance = mount(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style')
|
||||
).not.toEqual(
|
||||
expect.objectContaining({
|
||||
color: expect.any(String),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders no color styling for numeric value if value is higher than rangeMax', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].c = 500;
|
||||
args.colorMode = ColorMode.Labels;
|
||||
args.palette.params = {
|
||||
rangeMin: 0,
|
||||
rangeMax: 400,
|
||||
stops: [100, 200, 400],
|
||||
gradient: false,
|
||||
range: 'number',
|
||||
colors: ['red', 'yellow', 'green'],
|
||||
};
|
||||
|
||||
const instance = mount(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style')
|
||||
).not.toEqual(
|
||||
expect.objectContaining({
|
||||
color: expect.any(String),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders no color styling for numeric value if value is lower than rangeMin', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].c = -1;
|
||||
args.colorMode = ColorMode.Labels;
|
||||
args.palette.params = {
|
||||
rangeMin: 0,
|
||||
rangeMax: 400,
|
||||
stops: [100, 200, 400],
|
||||
gradient: false,
|
||||
range: 'number',
|
||||
colors: ['red', 'yellow', 'green'],
|
||||
};
|
||||
|
||||
const instance = mount(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style')
|
||||
).not.toEqual(
|
||||
expect.objectContaining({
|
||||
color: expect.any(String),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders the correct color styling for numeric value if user select auto detect max value', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].c = 500;
|
||||
args.colorMode = ColorMode.Labels;
|
||||
args.palette.params = {
|
||||
rangeMin: 20,
|
||||
rangeMax: Infinity,
|
||||
stops: [100, 200, 400],
|
||||
gradient: false,
|
||||
range: 'number',
|
||||
colors: ['red', 'yellow', 'green'],
|
||||
};
|
||||
|
||||
const instance = mount(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style')
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
color: 'green',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders the correct color styling for numeric value if user select auto detect min value', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].c = -1;
|
||||
args.colorMode = ColorMode.Labels;
|
||||
args.palette.params = {
|
||||
rangeMin: -Infinity,
|
||||
rangeMax: 400,
|
||||
stops: [-Infinity, 200, 400],
|
||||
gradient: false,
|
||||
range: 'number',
|
||||
colors: ['red', 'yellow', 'green'],
|
||||
};
|
||||
|
||||
const instance = mount(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={() => ({ convert: (x) => x } as IFieldFormat)}
|
||||
uiSettings={{ get: jest.fn() } as unknown as IUiSettingsClient}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
instance.find('[data-test-subj="lnsVisualizationContainer"]').first().prop('style')
|
||||
).toEqual(
|
||||
expect.objectContaining({
|
||||
color: 'red',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,172 +0,0 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import './expression.scss';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import classNames from 'classnames';
|
||||
import { IUiSettingsClient, ThemeServiceStart } from 'kibana/public';
|
||||
import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public';
|
||||
import type {
|
||||
ExpressionRenderDefinition,
|
||||
IInterpreterRenderHandlers,
|
||||
} from '../../../../../src/plugins/expressions/public';
|
||||
import {
|
||||
ColorMode,
|
||||
CustomPaletteState,
|
||||
PaletteOutput,
|
||||
} from '../../../../../src/plugins/charts/public';
|
||||
import { AutoScale } from './auto_scale';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { getContrastColor } from '../shared_components';
|
||||
import { EmptyPlaceholder } from '../../../../../src/plugins/charts/public';
|
||||
import { LensIconChartMetric } from '../assets/chart_metric';
|
||||
import type { FormatFactory } from '../../common';
|
||||
import type { MetricChartProps } from '../../common/expressions';
|
||||
export type { MetricChartProps, MetricState, MetricConfig } from '../../common/expressions';
|
||||
|
||||
export const getMetricChartRenderer = (
|
||||
formatFactory: FormatFactory,
|
||||
uiSettings: IUiSettingsClient,
|
||||
theme: ThemeServiceStart
|
||||
): ExpressionRenderDefinition<MetricChartProps> => ({
|
||||
name: 'lens_metric_chart_renderer',
|
||||
displayName: 'Metric chart',
|
||||
help: 'Metric chart renderer',
|
||||
validate: () => undefined,
|
||||
reuseDomNode: true,
|
||||
render: (domNode: Element, config: MetricChartProps, handlers: IInterpreterRenderHandlers) => {
|
||||
ReactDOM.render(
|
||||
<KibanaThemeProvider theme$={theme.theme$}>
|
||||
<I18nProvider>
|
||||
<MetricChart {...config} formatFactory={formatFactory} uiSettings={uiSettings} />
|
||||
</I18nProvider>
|
||||
</KibanaThemeProvider>,
|
||||
domNode,
|
||||
() => {
|
||||
handlers.done();
|
||||
}
|
||||
);
|
||||
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
|
||||
},
|
||||
});
|
||||
|
||||
function getColorStyling(
|
||||
value: number,
|
||||
colorMode: ColorMode,
|
||||
palette: PaletteOutput<CustomPaletteState> | undefined,
|
||||
isDarkTheme: boolean
|
||||
) {
|
||||
if (
|
||||
colorMode === ColorMode.None ||
|
||||
!palette?.params ||
|
||||
!palette?.params.colors?.length ||
|
||||
isNaN(value)
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const { rangeMin, rangeMax, stops, colors } = palette.params;
|
||||
|
||||
if (value > rangeMax) {
|
||||
return {};
|
||||
}
|
||||
if (value < rangeMin) {
|
||||
return {};
|
||||
}
|
||||
const cssProp = colorMode === ColorMode.Background ? 'backgroundColor' : 'color';
|
||||
let rawIndex = stops.findIndex((v) => v > value);
|
||||
|
||||
if (!isFinite(rangeMax) && value > stops[stops.length - 1]) {
|
||||
rawIndex = stops.length - 1;
|
||||
}
|
||||
|
||||
// in this case first stop is -Infinity
|
||||
if (!isFinite(rangeMin) && value < (isFinite(stops[0]) ? stops[0] : stops[1])) {
|
||||
rawIndex = 0;
|
||||
}
|
||||
|
||||
const colorIndex = rawIndex;
|
||||
|
||||
const color = colors[colorIndex];
|
||||
const styling = {
|
||||
[cssProp]: color,
|
||||
};
|
||||
if (colorMode === ColorMode.Background && color) {
|
||||
// set to "euiTextColor" for both light and dark color, depending on the theme
|
||||
styling.color = getContrastColor(color, isDarkTheme, 'euiTextColor', 'euiTextColor');
|
||||
}
|
||||
return styling;
|
||||
}
|
||||
|
||||
export function MetricChart({
|
||||
data,
|
||||
args,
|
||||
formatFactory,
|
||||
uiSettings,
|
||||
}: MetricChartProps & { formatFactory: FormatFactory; uiSettings: IUiSettingsClient }) {
|
||||
const { metricTitle, accessor, mode, colorMode, palette, titlePosition, textAlign, size } = args;
|
||||
const firstTable = Object.values(data.tables)[0];
|
||||
|
||||
const getEmptyState = () => (
|
||||
<VisualizationContainer className="lnsMetricExpression__container">
|
||||
<EmptyPlaceholder icon={LensIconChartMetric} />
|
||||
</VisualizationContainer>
|
||||
);
|
||||
|
||||
if (!accessor || !firstTable) {
|
||||
return getEmptyState();
|
||||
}
|
||||
|
||||
const column = firstTable.columns.find(({ id }) => id === accessor);
|
||||
const row = firstTable.rows[0];
|
||||
if (!column || !row) {
|
||||
return getEmptyState();
|
||||
}
|
||||
const rawValue = row[accessor];
|
||||
|
||||
// NOTE: Cardinality and Sum never receives "null" as value, but always 0, even for empty dataset.
|
||||
// Mind falsy values here as 0!
|
||||
if (!['number', 'string'].includes(typeof rawValue)) {
|
||||
return getEmptyState();
|
||||
}
|
||||
|
||||
const value =
|
||||
column && column.meta?.params
|
||||
? formatFactory(column.meta?.params).convert(rawValue)
|
||||
: Number(Number(rawValue).toFixed(3)).toString();
|
||||
|
||||
const color = getColorStyling(rawValue, colorMode, palette, uiSettings.get('theme:darkMode'));
|
||||
|
||||
return (
|
||||
<VisualizationContainer className="lnsMetricExpression__container" style={color}>
|
||||
<AutoScale
|
||||
key={value}
|
||||
titlePosition={titlePosition}
|
||||
textAlign={textAlign}
|
||||
size={size}
|
||||
minScale={mode === 'full' ? undefined : 0.05}
|
||||
>
|
||||
{mode === 'full' && (
|
||||
<div
|
||||
data-test-subj="lns_metric_title"
|
||||
className={classNames('lnsMetricExpression__title', {
|
||||
reverseOrder: ['bottom', 'right'].includes(titlePosition ?? ''),
|
||||
})}
|
||||
style={colorMode === ColorMode.Background ? color : undefined}
|
||||
>
|
||||
{metricTitle}
|
||||
</div>
|
||||
)}
|
||||
<div data-test-subj="lns_metric_value" className="lnsMetricExpression__value">
|
||||
{value}
|
||||
</div>
|
||||
</AutoScale>
|
||||
</VisualizationContainer>
|
||||
);
|
||||
}
|
|
@ -6,30 +6,20 @@
|
|||
*/
|
||||
|
||||
import type { CoreSetup } from 'kibana/public';
|
||||
import type { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import type { ChartsPluginSetup } from '../../../../../src/plugins/charts/public';
|
||||
import type { EditorFrameSetup } from '../types';
|
||||
import type { FormatFactory } from '../../common';
|
||||
|
||||
export interface MetricVisualizationPluginSetupPlugins {
|
||||
expressions: ExpressionsSetup;
|
||||
formatFactory: FormatFactory;
|
||||
editorFrame: EditorFrameSetup;
|
||||
charts: ChartsPluginSetup;
|
||||
}
|
||||
|
||||
export class MetricVisualization {
|
||||
setup(
|
||||
core: CoreSetup,
|
||||
{ expressions, formatFactory, editorFrame, charts }: MetricVisualizationPluginSetupPlugins
|
||||
) {
|
||||
setup(core: CoreSetup, { editorFrame, charts }: MetricVisualizationPluginSetupPlugins) {
|
||||
editorFrame.registerVisualization(async () => {
|
||||
const { getMetricVisualization, getMetricChartRenderer } = await import('../async_services');
|
||||
const { getMetricVisualization } = await import('../async_services');
|
||||
const palettes = await charts.palettes.getPalettes();
|
||||
|
||||
expressions.registerRenderer(() =>
|
||||
getMetricChartRenderer(formatFactory, core.uiSettings, core.theme)
|
||||
);
|
||||
return getMetricVisualization({ paletteService: palettes, theme: core.theme });
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButtonGroup } from '@elastic/eui';
|
||||
import { MetricState } from '../../../common/expressions';
|
||||
import { MetricState } from '../../../common/types';
|
||||
|
||||
export interface TitlePositionProps {
|
||||
state: MetricState;
|
||||
|
|
|
@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { ToolbarPopover, TooltipWrapper } from '../../shared_components';
|
||||
import { TitlePositionOptions } from './title_position_option';
|
||||
import { FramePublicAPI } from '../../types';
|
||||
import { MetricState } from '../../../common/expressions';
|
||||
import type { MetricState } from '../../../common/types';
|
||||
import { TextFormattingOptions } from './text_formatting_options';
|
||||
|
||||
export interface VisualOptionsPopoverProps {
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
import React, { memo } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, htmlIdGenerator } from '@elastic/eui';
|
||||
import type { VisualizationToolbarProps } from '../../types';
|
||||
import type { MetricState } from '../../../common/types';
|
||||
|
||||
import { AppearanceOptionsPopover } from './appearance_options_popover';
|
||||
import { MetricState } from '../../../common/expressions';
|
||||
|
||||
export const MetricToolbar = memo(function MetricToolbar(
|
||||
props: VisualizationToolbarProps<MetricState>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButtonIcon, EuiSuperSelect } from '@elastic/eui';
|
||||
import { MetricState } from '../../../common/expressions';
|
||||
import type { MetricState } from '../../../common/types';
|
||||
|
||||
export interface TitlePositionProps {
|
||||
state: MetricState;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiFormRow, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { MetricState } from '../../../common/expressions';
|
||||
import type { MetricState } from '../../../common/types';
|
||||
import { SizeOptions } from './size_options';
|
||||
import { AlignOptions } from './align_options';
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiButtonGroup, EuiFormRow } from '@elastic/eui';
|
||||
import { MetricState } from '../../../common/expressions';
|
||||
import type { MetricState } from '../../../common/types';
|
||||
|
||||
export interface TitlePositionProps {
|
||||
state: MetricState;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { SuggestionRequest, VisualizationSuggestion, TableSuggestion } from '../types';
|
||||
import type { MetricState } from '../../common/expressions';
|
||||
import type { MetricState } from '../../common/types';
|
||||
import { layerTypes } from '../../common';
|
||||
import { LensIconChartMetric } from '../assets/chart_metric';
|
||||
import { supportedTypes } from './visualization';
|
||||
|
|
|
@ -5,5 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './expression';
|
||||
export * from './visualization';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { getMetricVisualization } from './visualization';
|
||||
import { MetricState } from '../../common/expressions';
|
||||
import type { MetricState } from '../../common/types';
|
||||
import { layerTypes } from '../../common';
|
||||
import { createMockDatasource, createMockFramePublicAPI } from '../mocks';
|
||||
import { generateId } from '../id_generator';
|
||||
|
@ -286,36 +286,93 @@ describe('metric_visualization', () => {
|
|||
"chain": Array [
|
||||
Object {
|
||||
"arguments": Object {
|
||||
"accessor": Array [
|
||||
"a",
|
||||
"autoScale": Array [
|
||||
true,
|
||||
],
|
||||
"colorFullBackground": Array [
|
||||
true,
|
||||
],
|
||||
"colorMode": Array [
|
||||
"None",
|
||||
],
|
||||
"description": Array [
|
||||
"",
|
||||
"font": Array [
|
||||
Object {
|
||||
"chain": Array [
|
||||
Object {
|
||||
"arguments": Object {
|
||||
"align": Array [
|
||||
"center",
|
||||
],
|
||||
"lHeight": Array [
|
||||
127.5,
|
||||
],
|
||||
"size": Array [
|
||||
85,
|
||||
],
|
||||
"sizeUnit": Array [
|
||||
"px",
|
||||
],
|
||||
"weight": Array [
|
||||
"600",
|
||||
],
|
||||
},
|
||||
"function": "font",
|
||||
"type": "function",
|
||||
},
|
||||
],
|
||||
"type": "expression",
|
||||
},
|
||||
],
|
||||
"metricTitle": Array [
|
||||
"shazm",
|
||||
"labelFont": Array [
|
||||
Object {
|
||||
"chain": Array [
|
||||
Object {
|
||||
"arguments": Object {
|
||||
"align": Array [
|
||||
"center",
|
||||
],
|
||||
"lHeight": Array [
|
||||
40.5,
|
||||
],
|
||||
"size": Array [
|
||||
27,
|
||||
],
|
||||
"sizeUnit": Array [
|
||||
"px",
|
||||
],
|
||||
},
|
||||
"function": "font",
|
||||
"type": "function",
|
||||
},
|
||||
],
|
||||
"type": "expression",
|
||||
},
|
||||
],
|
||||
"mode": Array [
|
||||
"full",
|
||||
],
|
||||
"palette": Array [],
|
||||
"size": Array [
|
||||
"xl",
|
||||
],
|
||||
"textAlign": Array [
|
||||
"center",
|
||||
],
|
||||
"title": Array [
|
||||
"",
|
||||
],
|
||||
"titlePosition": Array [
|
||||
"labelPosition": Array [
|
||||
"bottom",
|
||||
],
|
||||
"metric": Array [
|
||||
Object {
|
||||
"chain": Array [
|
||||
Object {
|
||||
"arguments": Object {
|
||||
"accessor": Array [
|
||||
"a",
|
||||
],
|
||||
},
|
||||
"function": "visdimension",
|
||||
"type": "function",
|
||||
},
|
||||
],
|
||||
"type": "expression",
|
||||
},
|
||||
],
|
||||
"palette": Array [],
|
||||
"showLabels": Array [
|
||||
true,
|
||||
],
|
||||
},
|
||||
"function": "lens_metric_chart",
|
||||
"function": "metricVis",
|
||||
"type": "function",
|
||||
},
|
||||
],
|
||||
|
|
|
@ -8,23 +8,45 @@
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { render } from 'react-dom';
|
||||
import { Ast } from '@kbn/interpreter';
|
||||
import { ThemeServiceStart } from 'kibana/public';
|
||||
import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { ColorMode } from '../../../../../src/plugins/charts/common';
|
||||
import {
|
||||
ColorMode,
|
||||
CustomPaletteState,
|
||||
PaletteOutput,
|
||||
} from '../../../../../src/plugins/charts/common';
|
||||
import { PaletteRegistry } from '../../../../../src/plugins/charts/public';
|
||||
import { getSuggestions } from './metric_suggestions';
|
||||
import { LensIconChartMetric } from '../assets/chart_metric';
|
||||
import { Visualization, OperationMetadata, DatasourcePublicAPI } from '../types';
|
||||
import type { MetricConfig, MetricState } from '../../common/expressions';
|
||||
import type { MetricState } from '../../common/types';
|
||||
import { layerTypes } from '../../common';
|
||||
import { CUSTOM_PALETTE, shiftPalette } from '../shared_components';
|
||||
import { MetricDimensionEditor } from './dimension_editor';
|
||||
import { MetricToolbar } from './metric_config_panel';
|
||||
|
||||
interface MetricConfig extends Omit<MetricState, 'palette' | 'colorMode'> {
|
||||
title: string;
|
||||
description: string;
|
||||
metricTitle: string;
|
||||
mode: 'reduced' | 'full';
|
||||
colorMode: ColorMode;
|
||||
palette: PaletteOutput<CustomPaletteState>;
|
||||
}
|
||||
|
||||
export const supportedTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']);
|
||||
|
||||
const getFontSizeAndUnit = (fontSize: string) => {
|
||||
const [size, sizeUnit] = fontSize.split(/(\d+)/).filter(Boolean);
|
||||
return {
|
||||
size: Number(size),
|
||||
sizeUnit,
|
||||
};
|
||||
};
|
||||
|
||||
const toExpression = (
|
||||
paletteService: PaletteRegistry,
|
||||
state: MetricState,
|
||||
|
@ -56,22 +78,87 @@ const toExpression = (
|
|||
reverse: false,
|
||||
};
|
||||
|
||||
const fontSizes: Record<string, { size: number; sizeUnit: string }> = {
|
||||
xs: getFontSizeAndUnit(euiThemeVars.euiFontSizeXS),
|
||||
s: getFontSizeAndUnit(euiThemeVars.euiFontSizeS),
|
||||
m: getFontSizeAndUnit(euiThemeVars.euiFontSizeM),
|
||||
l: getFontSizeAndUnit(euiThemeVars.euiFontSizeL),
|
||||
xl: getFontSizeAndUnit(euiThemeVars.euiFontSizeXL),
|
||||
xxl: getFontSizeAndUnit(euiThemeVars.euiFontSizeXXL),
|
||||
};
|
||||
|
||||
const labelFont = fontSizes[state?.size || 'xl'];
|
||||
const labelToMetricFontSizeMap: Record<string, number> = {
|
||||
xs: fontSizes.xs.size * 2,
|
||||
s: fontSizes.m.size * 2.5,
|
||||
m: fontSizes.l.size * 2.5,
|
||||
l: fontSizes.xl.size * 2.5,
|
||||
xl: fontSizes.xxl.size * 2.5,
|
||||
xxl: fontSizes.xxl.size * 3,
|
||||
};
|
||||
const metricFontSize = labelToMetricFontSizeMap[state?.size || 'xl'];
|
||||
|
||||
return {
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'lens_metric_chart',
|
||||
function: 'metricVis',
|
||||
arguments: {
|
||||
title: [attributes?.title || ''],
|
||||
size: [state?.size || 'xl'],
|
||||
titlePosition: [state?.titlePosition || 'bottom'],
|
||||
textAlign: [state?.textAlign || 'center'],
|
||||
description: [attributes?.description || ''],
|
||||
metricTitle: [operation?.label || ''],
|
||||
accessor: [state.accessor],
|
||||
mode: [attributes?.mode || 'full'],
|
||||
labelPosition: [state?.titlePosition || 'bottom'],
|
||||
font: [
|
||||
{
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'font',
|
||||
arguments: {
|
||||
align: [state?.textAlign || 'center'],
|
||||
size: [metricFontSize],
|
||||
weight: ['600'],
|
||||
lHeight: [metricFontSize * 1.5],
|
||||
sizeUnit: [labelFont.sizeUnit],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
labelFont: [
|
||||
{
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'font',
|
||||
arguments: {
|
||||
align: [state?.textAlign || 'center'],
|
||||
size: [labelFont.size],
|
||||
lHeight: [labelFont.size * 1.5],
|
||||
sizeUnit: [labelFont.sizeUnit],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
metric: [
|
||||
{
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'visdimension',
|
||||
arguments: {
|
||||
accessor: [state.accessor],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
showLabels: [!attributes?.mode || attributes?.mode === 'full'],
|
||||
colorMode: !canColor ? [ColorMode.None] : [state?.colorMode || ColorMode.None],
|
||||
autoScale: [true],
|
||||
colorFullBackground: [true],
|
||||
palette:
|
||||
state?.colorMode && state?.colorMode !== ColorMode.None
|
||||
? [paletteService.get(CUSTOM_PALETTE).toExpression(paletteParams)]
|
||||
|
@ -81,6 +168,7 @@ const toExpression = (
|
|||
],
|
||||
};
|
||||
};
|
||||
|
||||
export const getMetricVisualization = ({
|
||||
paletteService,
|
||||
theme,
|
||||
|
|
|
@ -9,7 +9,6 @@ import type { CoreSetup } from 'kibana/server';
|
|||
import {
|
||||
xyChart,
|
||||
counterRate,
|
||||
metricChart,
|
||||
yAxisConfig,
|
||||
dataLayerConfig,
|
||||
referenceLineLayerConfig,
|
||||
|
@ -38,7 +37,6 @@ export const setupExpressions = (
|
|||
[
|
||||
xyChart,
|
||||
counterRate,
|
||||
metricChart,
|
||||
yAxisConfig,
|
||||
dataLayerConfig,
|
||||
referenceLineLayerConfig,
|
||||
|
|
|
@ -711,19 +711,13 @@
|
|||
"xpack.lens.layerPanel.missingDataView": "データビューが見つかりません",
|
||||
"xpack.lens.legacyUrlConflict.objectNoun": "レンズビジュアライゼーション",
|
||||
"xpack.lens.lensSavedObjectLabel": "レンズビジュアライゼーション",
|
||||
"xpack.lens.metric.accessor.help": "値が表示されている列",
|
||||
"xpack.lens.metric.addLayer": "ビジュアライゼーションレイヤーを追加",
|
||||
"xpack.lens.metric.colorMode.help": "色を変更するメトリックの部分",
|
||||
"xpack.lens.metric.dynamicColoring.background": "塗りつぶし",
|
||||
"xpack.lens.metric.dynamicColoring.label": "値別の色",
|
||||
"xpack.lens.metric.dynamicColoring.none": "なし",
|
||||
"xpack.lens.metric.dynamicColoring.text": "テキスト",
|
||||
"xpack.lens.metric.groupLabel": "目標値と単一の値",
|
||||
"xpack.lens.metric.label": "メトリック",
|
||||
"xpack.lens.metric.metricTitle.help": "表示されるメトリックのタイトル。",
|
||||
"xpack.lens.metric.mode.help": "グラフの表示モード。減らすと、最小サイズ内でメトリックのみが表示されます",
|
||||
"xpack.lens.metric.palette.help": "値の色を指定します",
|
||||
"xpack.lens.metric.title.help": "グラフタイトル。",
|
||||
"xpack.lens.pageTitle": "レンズ",
|
||||
"xpack.lens.paletteHeatmapGradient.customize": "編集",
|
||||
"xpack.lens.paletteHeatmapGradient.customizeLong": "パレットを編集",
|
||||
|
|
|
@ -717,19 +717,13 @@
|
|||
"xpack.lens.layerPanel.missingDataView": "找不到数据视图",
|
||||
"xpack.lens.legacyUrlConflict.objectNoun": "Lens 可视化",
|
||||
"xpack.lens.lensSavedObjectLabel": "Lens 可视化",
|
||||
"xpack.lens.metric.accessor.help": "正显示值的列",
|
||||
"xpack.lens.metric.addLayer": "添加可视化图层",
|
||||
"xpack.lens.metric.colorMode.help": "指标的哪部分要上色",
|
||||
"xpack.lens.metric.dynamicColoring.background": "填充",
|
||||
"xpack.lens.metric.dynamicColoring.label": "按值上色",
|
||||
"xpack.lens.metric.dynamicColoring.none": "无",
|
||||
"xpack.lens.metric.dynamicColoring.text": "文本",
|
||||
"xpack.lens.metric.groupLabel": "目标值和单值",
|
||||
"xpack.lens.metric.label": "指标",
|
||||
"xpack.lens.metric.metricTitle.help": "显示的指标标题。",
|
||||
"xpack.lens.metric.mode.help": "图表的显示模式 - 缩减模式将仅显示指标本身,无最小大小",
|
||||
"xpack.lens.metric.palette.help": "为值提供颜色",
|
||||
"xpack.lens.metric.title.help": "图标标题。",
|
||||
"xpack.lens.pageTitle": "Lens",
|
||||
"xpack.lens.paletteHeatmapGradient.customize": "编辑",
|
||||
"xpack.lens.paletteHeatmapGradient.customizeLong": "编辑调色板",
|
||||
|
|
|
@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
await PageObjects.lens.switchToVisualization('lnsMetric');
|
||||
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('mtrVis');
|
||||
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
|
||||
};
|
||||
|
||||
|
@ -304,10 +304,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
await PageObjects.lens.switchToVisualization('lnsMetric');
|
||||
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('mtrVis');
|
||||
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
|
||||
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('mtrVis');
|
||||
await testSubjects.click('lnsApp_saveButton');
|
||||
|
||||
const hasOptions = await testSubjects.exists('add-to-dashboard-options');
|
||||
|
@ -350,10 +350,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
await PageObjects.lens.switchToVisualization('lnsMetric');
|
||||
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('mtrVis');
|
||||
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
|
||||
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('mtrVis');
|
||||
await testSubjects.click('lnsApp_saveButton');
|
||||
|
||||
const hasOptions = await testSubjects.exists('add-to-dashboard-options');
|
||||
|
|
|
@ -37,7 +37,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('should switch to gauge and render a gauge with default values', async () => {
|
||||
await PageObjects.lens.switchToVisualization('horizontalBullet', 'gauge');
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('gaugeChart');
|
||||
const elementWithInfo = await find.byCssSelector('.echScreenReaderOnly');
|
||||
const textContent = await elementWithInfo.getAttribute('textContent');
|
||||
expect(textContent).to.contain('Average of bytes'); // it gets default title
|
||||
|
@ -63,25 +63,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.lens.retrySetValue('lnsToolbarGaugeLabelMinor-select', 'custom', {});
|
||||
await PageObjects.lens.retrySetValue('lnsToolbarGaugeLabelMinor', 'custom subtitle');
|
||||
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('gaugeChart');
|
||||
await PageObjects.lens.openDimensionEditor(
|
||||
'lnsGauge_goalDimensionPanel > lns-empty-dimension'
|
||||
);
|
||||
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('gaugeChart');
|
||||
await PageObjects.lens.closeDimensionEditor();
|
||||
await PageObjects.lens.openDimensionEditor(
|
||||
'lnsGauge_minDimensionPanel > lns-empty-dimension-suggested-value'
|
||||
);
|
||||
await PageObjects.lens.retrySetValue('lns-indexPattern-static_value-input', '1000');
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('gaugeChart');
|
||||
await PageObjects.lens.closeDimensionEditor();
|
||||
|
||||
await PageObjects.lens.openDimensionEditor(
|
||||
'lnsGauge_maxDimensionPanel > lns-empty-dimension-suggested-value'
|
||||
);
|
||||
await PageObjects.lens.retrySetValue('lns-indexPattern-static_value-input', '25000');
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('gaugeChart');
|
||||
await PageObjects.lens.closeDimensionEditor();
|
||||
|
||||
const elementWithInfo = await find.byCssSelector('.echScreenReaderOnly');
|
||||
|
|
|
@ -37,21 +37,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await testSubjects.setValue('lnsPalettePanel_dynamicColoring_range_value_1', '21000', {
|
||||
clearWithKeyboard: true,
|
||||
});
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('mtrVis');
|
||||
const styleObj = await PageObjects.lens.getMetricStyle();
|
||||
expect(styleObj.color).to.be('rgb(32, 146, 128)');
|
||||
});
|
||||
|
||||
it('should change the color when reverting the palette', async () => {
|
||||
await testSubjects.click('lnsPalettePanel_dynamicColoring_reverseColors');
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('mtrVis');
|
||||
const styleObj = await PageObjects.lens.getMetricStyle();
|
||||
expect(styleObj.color).to.be('rgb(204, 86, 66)');
|
||||
});
|
||||
|
||||
it('should reset the color stops when changing palette to a predefined one', async () => {
|
||||
await PageObjects.lens.changePaletteTo('temperature');
|
||||
await PageObjects.lens.waitForVisualization();
|
||||
await PageObjects.lens.waitForVisualization('mtrVis');
|
||||
const styleObj = await PageObjects.lens.getMetricStyle();
|
||||
expect(styleObj.color).to.be('rgb(235, 239, 245)');
|
||||
});
|
||||
|
|
|
@ -1054,8 +1054,8 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
* @param count - expected count of metric
|
||||
*/
|
||||
async assertMetric(title: string, count: string) {
|
||||
await this.assertExactText('[data-test-subj="lns_metric_title"]', title);
|
||||
await this.assertExactText('[data-test-subj="lns_metric_value"]', count);
|
||||
await this.assertExactText('[data-test-subj="metric_label"]', title);
|
||||
await this.assertExactText('[data-test-subj="metric_value"]', count);
|
||||
},
|
||||
|
||||
async setMetricDynamicColoring(coloringType: 'none' | 'labels' | 'background') {
|
||||
|
@ -1063,7 +1063,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
},
|
||||
|
||||
async getMetricStyle() {
|
||||
const el = await testSubjects.find('lnsVisualizationContainer');
|
||||
const el = await testSubjects.find('metric_value');
|
||||
const styleString = await el.getAttribute('style');
|
||||
return styleString.split(';').reduce<Record<string, string>>((memo, cssLine) => {
|
||||
const [prop, value] = cssLine.split(':');
|
||||
|
@ -1152,9 +1152,11 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
expect(focusedElementText).to.eql(name);
|
||||
},
|
||||
|
||||
async waitForVisualization() {
|
||||
async waitForVisualization(visDataTestSubj?: string) {
|
||||
async function getRenderingCount() {
|
||||
const visualizationContainer = await testSubjects.find('lnsVisualizationContainer');
|
||||
const visualizationContainer = await testSubjects.find(
|
||||
visDataTestSubj || 'lnsVisualizationContainer'
|
||||
);
|
||||
const renderingCount = await visualizationContainer.getAttribute('data-rendering-count');
|
||||
return Number(renderingCount);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue