[Lens] make metric visualization scrollable when too tall (#138178)

This commit is contained in:
Andrew Tate 2022-08-16 09:38:53 -05:00 committed by GitHub
parent 0f974620c0
commit 387469be14
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 88 additions and 33 deletions

View file

@ -754,6 +754,7 @@ describe('MetricVisComponent', function () {
/>
)
.find('div')
.at(0)
.props() as HtmlAttributes & { css: { styles: string } }
).css.styles;
@ -763,6 +764,7 @@ describe('MetricVisComponent', function () {
width: 100%;
max-height: 100%;
max-width: 100%;
overflow-y: auto;
"
`);
@ -772,6 +774,7 @@ describe('MetricVisComponent', function () {
width: 300px;
max-height: 100%;
max-width: 100%;
overflow-y: auto;
"
`);
@ -781,6 +784,7 @@ describe('MetricVisComponent', function () {
width: 1000px;
max-height: 100%;
max-width: 100%;
overflow-y: auto;
"
`);
});

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import React, { useCallback, useEffect, useRef } from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import numeral from '@elastic/numeral';
import { i18n } from '@kbn/i18n';
@ -34,6 +34,7 @@ import type { FieldFormatConvertFunction } from '@kbn/field-formats-plugin/commo
import { CUSTOM_PALETTE } from '@kbn/coloring';
import { css } from '@emotion/react';
import { euiThemeVars } from '@kbn/ui-theme';
import { useResizeObserver } from '@elastic/eui';
import { VisParams } from '../../common';
import {
getPaletteService,
@ -307,6 +308,20 @@ export const MetricVis = ({
pixelWidth = grid[0]?.length * maxTileSideLength;
}
const [scrollChildHeight, setScrollChildHeight] = useState<string>('100%');
const scrollContainerRef = useRef<HTMLDivElement>(null);
const scrollDimensions = useResizeObserver(scrollContainerRef.current);
useEffect(() => {
const minTileHeight = 64; // TODO - magic number from the @elastic/charts side. would be nice to deduplicate
const minimumRequiredVerticalSpace = minTileHeight * grid.length;
setScrollChildHeight(
(scrollDimensions.height ?? -Infinity) > minimumRequiredVerticalSpace
? '100%'
: `${minimumRequiredVerticalSpace}px`
);
}, [grid.length, scrollDimensions.height]);
// force chart to re-render to circumvent a charts bug
const magicKey = useRef(0);
useEffect(() => {
@ -315,45 +330,53 @@ export const MetricVis = ({
return (
<div
ref={scrollContainerRef}
css={css`
height: ${pixelHeight ? `${pixelHeight}px` : '100%'};
width: ${pixelWidth ? `${pixelWidth}px` : '100%'};
max-height: 100%;
max-width: 100%;
overflow-y: auto;
`}
>
<Chart key={magicKey.current}>
<Settings
theme={[
{
background: { color: 'transparent' },
metric: {
background: defaultColor,
barBackground: euiThemeVars.euiColorLightShade,
<div
css={css`
height: ${scrollChildHeight};
`}
>
<Chart key={magicKey.current}>
<Settings
theme={[
{
background: { color: 'transparent' },
metric: {
background: defaultColor,
barBackground: euiThemeVars.euiColorLightShade,
},
},
},
chartTheme,
]}
onRenderChange={onRenderChange}
onElementClick={(events) => {
if (!filterable) {
return;
}
events.forEach((event) => {
if (isMetricElementEvent(event)) {
const colIdx = breakdownByColumn
? data.columns.findIndex((col) => col === breakdownByColumn)
: data.columns.findIndex((col) => col === primaryMetricColumn);
const rowLength = grid[0].length;
fireEvent(
buildFilterEvent(event.rowIndex * rowLength + event.columnIndex, colIdx, data)
);
chartTheme,
]}
onRenderChange={onRenderChange}
onElementClick={(events) => {
if (!filterable) {
return;
}
});
}}
/>
<Metric id="metric" data={grid} />
</Chart>
events.forEach((event) => {
if (isMetricElementEvent(event)) {
const colIdx = breakdownByColumn
? data.columns.findIndex((col) => col === breakdownByColumn)
: data.columns.findIndex((col) => col === primaryMetricColumn);
const rowLength = grid[0].length;
fireEvent(
buildFilterEvent(event.rowIndex * rowLength + event.columnIndex, colIdx, data)
);
}
});
}}
/>
<Metric id="metric" data={grid} />
</Chart>
</div>
</div>
);
};

View file

@ -174,7 +174,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
const colorPicker = await testSubjects.find('euiColorPickerAnchor');
colorPicker.clearValue();
await colorPicker.clearValue();
await colorPicker.type('#000000');
await PageObjects.lens.waitForVisualization('mtrVis');
@ -213,10 +213,38 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.lens.waitForVisualization('mtrVis');
expect((await getMetricData()).map(({ color }) => color)).to.eql(expectedDynamicColors); // colors shouldn't change
await PageObjects.lens.closePaletteEditor();
await PageObjects.lens.closeDimensionEditor();
});
it('makes visualization scrollable if too tall', async () => {
await PageObjects.lens.removeDimension('lnsMetric_breakdownByDimensionPanel');
await PageObjects.lens.configureDimension({
dimension: 'lnsMetric_breakdownByDimensionPanel > lns-empty-dimension',
operation: 'date_histogram',
field: '@timestamp',
keepOpen: true,
});
await testSubjects.setValue('lnsMetric_max_cols', '1');
await PageObjects.lens.closeDimensionEditor();
const tiles = await getMetricTiles();
const lastTile = tiles[tiles.length - 1];
const initialPosition = await lastTile.getPosition();
await lastTile.scrollIntoViewIfNecessary();
const scrolledPosition = await lastTile.getPosition();
expect(scrolledPosition.y).to.be.below(initialPosition.y);
});
it("doesn't error with empty formula", async () => {
await PageObjects.lens.closePaletteEditor();
await PageObjects.lens.openDimensionEditor(
'lnsMetric_primaryMetricDimensionPanel > lns-dimensionTrigger'
);
await PageObjects.lens.switchToFormula();
await PageObjects.lens.typeFormula('');