Skip to content
Merged

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

Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({
defaultMessage: 'bucket dimension configuration',
}),
},
autoScale: {
types: ['boolean'],
help: i18n.translate('expressionMetricVis.function.autoScale.help', {
defaultMessage: 'Enable auto scale',
}),
required: false,
},
},
fn(input, args, handlers) {
if (args.percentageMode && !args.palette?.params) {
Expand Down Expand Up @@ -117,6 +124,7 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({
labelColor: args.colorMode === ColorMode.Labels,
...args.font,
},
autoScale: args.autoScale,
},
dimensions: {
metrics: args.metric,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface MetricArguments {
font: Style;
metric: ExpressionValueVisDimension[];
bucket?: ExpressionValueVisDimension;
autoScale?: boolean;
}

export type MetricInput = Datatable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface MetricVisParam {
palette?: CustomPaletteState;
labels: Labels;
style: MetricStyle;
autoScale?: boolean;
}

export interface VisParams {
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
// mtrChart__legend-isLoading

.mtrVis {
@include euiScrollBar;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need these changes? I don't notice any difference

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we use VisualizationContainer here with styles which includes styles for scrollbar, but styles of VisualizationContainer loaded only when we open visualization first (because we load styles here src\plugins\visualizations\public\vis_async.ts). If we open metric directly in canvas you will see so that we don't have scroll at all because we don't load styles for VisualizationContainer. So to be sure that we will have correct scroll I added these styles here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks 👍

height: 100%;
width: 100%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
flex-wrap: wrap;
overflow: auto;
}

.mtrVis__value {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ExpressionValueVisDimension } from '../../../../visualizations/public';
import { formatValue, shouldApplyColor } from '../utils';
import { getColumnByAccessor } from '../utils/accessor';
import { needsLightText } from '../utils/palette';
import { withAutoScale } from './with_auto_scale';

import './metric.scss';

Expand All @@ -27,6 +28,8 @@ export interface MetricVisComponentProps {
renderComplete: () => void;
}

const AutoScaleMetricVisValue = withAutoScale(MetricVisValue);

class MetricVisComponent extends Component<MetricVisComponentProps> {
private getColor(value: number, paletteParams: CustomPaletteState) {
return getPaletteService().get('custom')?.getColorForValue?.(value, paletteParams, {
Expand Down Expand Up @@ -108,8 +111,12 @@ class MetricVisComponent extends Component<MetricVisComponentProps> {
};

private renderMetric = (metric: MetricOptions, index: number) => {
const MetricComponent = this.props.visParams.metric.autoScale
? AutoScaleMetricVisValue
: MetricVisValue;

return (
<MetricVisValue
<MetricComponent
key={index}
metric={metric}
style={this.props.visParams.metric.style}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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 { css } from '@emotion/react';
import { euiThemeVars } from '@kbn/ui-theme';

export const autoScaleWrapperStyle = css({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
maxWidth: '100%',
maxHeight: '100%',
overflow: 'hidden',
lineHeight: euiThemeVars.euiLineHeight,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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 { computeScale, withAutoScale } from './with_auto_scale';
import { mount } 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('withAutoScale', () => {
it('renders', () => {
const Component = () => <h1>Hoi!</h1>;
const WrappedComponent = withAutoScale(Component);
expect(mount(<WrappedComponent />)).toMatchSnapshot();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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, { useRef, useEffect, useState, ComponentType, useMemo } from 'react';
import { throttle } from 'lodash';
import { useResizeObserver } from '@elastic/eui';
import { autoScaleWrapperStyle } from './with_auto_scale.styles';

interface AutoScaleParams {
minScale?: number;
}
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);
}

export function withAutoScale<T>(
WrappedComponent: ComponentType<T>,
autoScaleParams?: AutoScaleParams
) {
return (props: T) => {
// An initial scale of 0 means we always redraw
// at least once, which is sub-optimal, but it
// prevents an annoying flicker.
const [scale, setScale] = useState(0);
const parentRef = useRef<HTMLDivElement>(null);
const childrenRef = useRef<HTMLDivElement>(null);
const parentDimensions = useResizeObserver(parentRef.current);

const scaleFn = useMemo(
() =>
throttle(() => {
const newScale = computeScale(
{ clientHeight: parentDimensions.height, clientWidth: parentDimensions.width },
childrenRef.current,
autoScaleParams?.minScale
);

// Prevent an infinite render loop
if (scale !== newScale) {
setScale(newScale);
}
}),
[parentDimensions, setScale, scale]
);

useEffect(() => {
scaleFn();
}, [scaleFn]);

return (
<div ref={parentRef} css={autoScaleWrapperStyle}>
<div
ref={childrenRef}
style={{
transform: `scale(${scale || 0})`,
}}
>
<WrappedComponent {...props} />
</div>
</div>
);
};
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"labels":{"show":true},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}}
{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"show":true},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"labels":{"show":true},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}}
{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"labels":{"show":true},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}}
Loading