mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Lens] Workspace panel dimensions by chart type (#168651)
## Summary Close https://github.com/elastic/kibana/issues/136993 All charts remain the same except the following ## Metric Each tile gets `200px` if there are multiple and `300px` if it's a single. The default background is EUI empty. <img width="600" alt="Screenshot 2023-10-30 at 3 55 33 PM" src="74d7e6dc
-c90a-4f60-bf56-94ab1556ad42"> <img width="600" alt="Screenshot 2023-10-30 at 3 56 36 PM" src="3254160a
-b18a-4c04-b059-f20b8f1f246a"> ## XY Vertical time-series charts have `16:9` ratio but will stretch to any available width and won't shrink below 300px height.3e056ae8
-bc65-4851-9ad9-6c8a81bdf58a ## Gauge Gauge gets `600px` for the long side, `300px` for the short side. <img width="600" alt="Screenshot 2023-11-28 at 11 22 20 AM" src="2b774515
-f060-4c26-a0aa-efdeebfff0e5"> <img width="600" alt="Screenshot 2023-11-28 at 11 22 33 AM" src="62181021
-d88a-4cb6-862b-42768a2df13e"> ## Known issues - [ ] text resizing on the metric https://github.com/elastic/elastic-charts/issues/22380162f461
-b544-44a9-971c-b2a0265d7d3a - [x] gauge isn't showing veil on willRender ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios Flaky test runner: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4755 --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: nickofthyme <nicholas.partridge@elastic.co> Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co> Co-authored-by: Marta Bondyra <4283304+mbondyra@users.noreply.github.com>
This commit is contained in:
parent
2a44f75de9
commit
e6a5647ea1
53 changed files with 1063 additions and 245 deletions
|
@ -52,7 +52,7 @@ pageLoadAssetSize:
|
|||
expressionLegacyMetricVis: 23121
|
||||
expressionMetric: 22238
|
||||
expressionMetricVis: 23121
|
||||
expressionPartitionVis: 28000
|
||||
expressionPartitionVis: 29000
|
||||
expressionRepeatImage: 22341
|
||||
expressionRevealImage: 25675
|
||||
expressions: 140958
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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, { useEffect } from 'react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import fastIsEqual from 'fast-deep-equal';
|
||||
import { useResizeObserver } from '@elastic/eui';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import type { ChartSizeSpec } from './types';
|
||||
|
||||
/**
|
||||
* This hook is used to show a veil over the chart while it is being resized
|
||||
* in response to a change in the container dimensions.
|
||||
*
|
||||
* It is only relevant if client dimensions are being requested based on chart configuration.
|
||||
*
|
||||
* This whole feature is a nice-to-have. If it proves to be a source of bugs,
|
||||
* we can consider removing it and accepting the aesthetic drawback.
|
||||
*/
|
||||
export function useSizeTransitionVeil(
|
||||
chartSizeSpec: ChartSizeSpec,
|
||||
setChartSize: (d: ChartSizeSpec) => void,
|
||||
// should be retrieved from handlers.shouldUseSizeTransitionVeil function
|
||||
shouldUseVeil: boolean
|
||||
) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const containerSize = useResizeObserver(containerRef.current);
|
||||
const currentContainerSize = useRef<{ width: number; height: number }>(containerSize);
|
||||
|
||||
// This useEffect hook is here to make up for the fact that the initial container size
|
||||
// is not correctly reported by the useResizeObserver hook (see https://github.com/elastic/eui/issues/7458).
|
||||
useEffect(() => {
|
||||
currentContainerSize.current = {
|
||||
width: containerRef.current?.clientWidth ?? 0,
|
||||
height: containerRef.current?.clientHeight ?? 0,
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [showVeil, setShowVeil] = useState(false);
|
||||
const currentChartSizeSpec = useRef<ChartSizeSpec>();
|
||||
const specJustChanged = useRef<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!fastIsEqual(containerSize, currentContainerSize.current) && specJustChanged.current) {
|
||||
// If the container size has changed, we need to show the veil to hide the chart since it
|
||||
// be rendered based on the previous container size before being updated to the current size.
|
||||
//
|
||||
// 1. we show the veil
|
||||
// 2. the charts library will render the chart based on the previous container dimensions
|
||||
// 3. the charts library will resize the chart to the updated container dimensions
|
||||
// 4. we hide the veil
|
||||
setShowVeil(true);
|
||||
currentContainerSize.current = containerSize;
|
||||
}
|
||||
}, [setShowVeil, containerSize]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!fastIsEqual(chartSizeSpec, currentChartSizeSpec.current)) {
|
||||
setChartSize(chartSizeSpec);
|
||||
currentChartSizeSpec.current = chartSizeSpec;
|
||||
specJustChanged.current = true;
|
||||
|
||||
setTimeout(() => {
|
||||
specJustChanged.current = false;
|
||||
}, 500);
|
||||
}
|
||||
}, [chartSizeSpec, setChartSize]);
|
||||
|
||||
const onResize = useCallback(() => {
|
||||
setShowVeil(false);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
veil: (
|
||||
<div
|
||||
css={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
backgroundColor: euiThemeVars.euiColorEmptyShade,
|
||||
position: 'absolute',
|
||||
zIndex: 1,
|
||||
display: shouldUseVeil && showVeil ? 'block' : 'none',
|
||||
}}
|
||||
/>
|
||||
),
|
||||
onResize,
|
||||
containerRef,
|
||||
};
|
||||
}
|
|
@ -12,5 +12,7 @@ export {
|
|||
getOverridesFor,
|
||||
isOnAggBasedEditor,
|
||||
} from './utils';
|
||||
export type { Simplify, MakeOverridesSerializable } from './types';
|
||||
export type { Simplify, MakeOverridesSerializable, ChartSizeSpec, ChartSizeEvent } from './types';
|
||||
export { isChartSizeEvent } from './types';
|
||||
export { getColorCategories } from './color_categories';
|
||||
export { useSizeTransitionVeil } from './chart_size_transition_veil';
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
"node",
|
||||
"@emotion/react/types/css-prop",
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
|
@ -17,5 +19,6 @@
|
|||
"@kbn/core-execution-context-common",
|
||||
"@kbn/expressions-plugin",
|
||||
"@kbn/data-plugin",
|
||||
"@kbn/ui-theme",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { ExpressionRendererEvent } from '@kbn/expressions-plugin/public';
|
||||
import React from 'react';
|
||||
|
||||
export type Simplify<T> = { [KeyType in keyof T]: T[KeyType] } & {};
|
||||
|
@ -26,3 +27,27 @@ export type MakeOverridesSerializable<T> = {
|
|||
? MakeOverridesSerializable<T[KeyType]>
|
||||
: NonNullable<T[KeyType]>;
|
||||
};
|
||||
|
||||
export interface ChartSizeEvent extends ExpressionRendererEvent {
|
||||
name: 'chartSize';
|
||||
data: ChartSizeSpec;
|
||||
}
|
||||
|
||||
export type ChartSizeUnit = 'pixels' | 'percentage';
|
||||
|
||||
interface ChartSizeDimensions {
|
||||
x?: { value: number; unit: ChartSizeUnit };
|
||||
y?: { value: number; unit: ChartSizeUnit };
|
||||
}
|
||||
|
||||
export interface ChartSizeSpec {
|
||||
// if maxDimensions are provided, the aspect ratio will be computed from them
|
||||
maxDimensions?: ChartSizeDimensions;
|
||||
minDimensions?: ChartSizeDimensions;
|
||||
aspectRatio?: { x: number; y: number };
|
||||
}
|
||||
|
||||
export function isChartSizeEvent(event: ExpressionRendererEvent): event is ChartSizeEvent {
|
||||
const expectedName: ChartSizeEvent['name'] = 'chartSize';
|
||||
return event.name === expectedName;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import type { PersistedState } from '@kbn/visualizations-plugin/public';
|
|||
import type { ChartsPluginSetup } from '@kbn/charts-plugin/public';
|
||||
import type { IFieldFormat, SerializedFieldFormat } from '@kbn/field-formats-plugin/common';
|
||||
import type { AllowedSettingsOverrides, AllowedChartOverrides } from '@kbn/charts-plugin/common';
|
||||
import type { ChartSizeSpec } from '@kbn/chart-expressions-common';
|
||||
import type { AllowedGaugeOverrides, GaugeExpressionProps } from './expression_functions';
|
||||
|
||||
export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat;
|
||||
|
@ -22,4 +23,6 @@ export type GaugeRenderProps = GaugeExpressionProps & {
|
|||
renderComplete: () => void;
|
||||
uiState: PersistedState;
|
||||
overrides?: AllowedGaugeOverrides & AllowedSettingsOverrides & AllowedChartOverrides;
|
||||
shouldUseVeil: boolean;
|
||||
setChartSize: (d: ChartSizeSpec) => void;
|
||||
};
|
||||
|
|
|
@ -536,6 +536,7 @@ exports[`GaugeComponent renders the chart 1`] = `
|
|||
/>
|
||||
}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
theme={
|
||||
Array [
|
||||
Object {
|
||||
|
|
|
@ -102,6 +102,8 @@ describe('GaugeComponent', function () {
|
|||
paletteService: await paletteThemeService.getPalettes(),
|
||||
uiState,
|
||||
renderComplete: jest.fn(),
|
||||
setChartSize: jest.fn(),
|
||||
shouldUseVeil: false,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -12,7 +12,11 @@ import type { PaletteOutput } from '@kbn/coloring';
|
|||
import { FieldFormat } from '@kbn/field-formats-plugin/common';
|
||||
import type { CustomPaletteState } from '@kbn/charts-plugin/public';
|
||||
import { EmptyPlaceholder } from '@kbn/charts-plugin/public';
|
||||
import { getOverridesFor } from '@kbn/chart-expressions-common';
|
||||
import {
|
||||
type ChartSizeSpec,
|
||||
getOverridesFor,
|
||||
useSizeTransitionVeil,
|
||||
} from '@kbn/chart-expressions-common';
|
||||
import { isVisDimension } from '@kbn/visualizations-plugin/common/utils';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
|
@ -178,6 +182,8 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
|||
chartsThemeService,
|
||||
renderComplete,
|
||||
overrides,
|
||||
shouldUseVeil,
|
||||
setChartSize,
|
||||
}) => {
|
||||
const {
|
||||
shape: gaugeType,
|
||||
|
@ -253,6 +259,26 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
|||
[renderComplete]
|
||||
);
|
||||
|
||||
const chartSizeSpec: ChartSizeSpec = {
|
||||
maxDimensions: {
|
||||
...(gaugeType === GaugeShapes.HORIZONTAL_BULLET
|
||||
? {
|
||||
x: { value: 600, unit: 'pixels' },
|
||||
y: { value: 300, unit: 'pixels' },
|
||||
}
|
||||
: {
|
||||
y: { value: 600, unit: 'pixels' },
|
||||
x: { value: 300, unit: 'pixels' },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const { veil, onResize, containerRef } = useSizeTransitionVeil(
|
||||
chartSizeSpec,
|
||||
setChartSize,
|
||||
shouldUseVeil
|
||||
);
|
||||
|
||||
const table = data;
|
||||
const accessors = getAccessorsFromArgs(args, table.columns);
|
||||
|
||||
|
@ -359,7 +385,8 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
|||
: {};
|
||||
|
||||
return (
|
||||
<div className="gauge__wrapper">
|
||||
<div className="gauge__wrapper" ref={containerRef}>
|
||||
{veil}
|
||||
<Chart {...getOverridesFor(overrides, 'chart')}>
|
||||
<Settings
|
||||
noResults={<EmptyPlaceholder icon={icon} renderComplete={onRenderChange} />}
|
||||
|
@ -369,6 +396,7 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
|||
ariaLabel={args.ariaLabel}
|
||||
ariaUseDefaultSummary={!args.ariaLabel}
|
||||
onRenderChange={onRenderChange}
|
||||
onResize={onResize}
|
||||
locale={i18n.getLocale()}
|
||||
{...getOverridesFor(overrides, 'settings')}
|
||||
/>
|
||||
|
|
|
@ -13,7 +13,11 @@ import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
|||
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers';
|
||||
import { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { extractContainerType, extractVisualizationType } from '@kbn/chart-expressions-common';
|
||||
import {
|
||||
ChartSizeEvent,
|
||||
extractContainerType,
|
||||
extractVisualizationType,
|
||||
} from '@kbn/chart-expressions-common';
|
||||
import { ExpressionGaugePluginStart } from '../plugin';
|
||||
import { EXPRESSION_GAUGE_NAME, GaugeExpressionProps, GaugeShapes } from '../../common';
|
||||
import { getFormatService, getPaletteService } from '../services';
|
||||
|
@ -66,16 +70,27 @@ export const gaugeRenderer: (
|
|||
handlers.done();
|
||||
};
|
||||
|
||||
const setChartSize = (chartSizeSpec: ChartSizeEvent['data']) => {
|
||||
const event: ChartSizeEvent = {
|
||||
name: 'chartSize',
|
||||
data: chartSizeSpec,
|
||||
};
|
||||
|
||||
handlers.event(event);
|
||||
};
|
||||
|
||||
const { GaugeComponent } = await import('../components/gauge_component');
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<div className="gauge-container" data-test-subj="gaugeChart">
|
||||
<GaugeComponent
|
||||
{...config}
|
||||
setChartSize={setChartSize}
|
||||
formatFactory={getFormatService().deserialize}
|
||||
chartsThemeService={plugins.charts.theme}
|
||||
paletteService={getPaletteService()}
|
||||
renderComplete={renderComplete}
|
||||
shouldUseVeil={handlers.shouldUseSizeTransitionVeil()}
|
||||
uiState={handlers.uiState as PersistedState}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -14,7 +14,11 @@ import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
|||
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers';
|
||||
import { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { extractContainerType, extractVisualizationType } from '@kbn/chart-expressions-common';
|
||||
import {
|
||||
ChartSizeEvent,
|
||||
extractContainerType,
|
||||
extractVisualizationType,
|
||||
} from '@kbn/chart-expressions-common';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { MultiFilterEvent } from '../../common/types';
|
||||
import { ExpressionHeatmapPluginStart } from '../plugin';
|
||||
|
@ -78,6 +82,18 @@ export const heatmapRenderer: (
|
|||
handlers.done();
|
||||
};
|
||||
|
||||
const chartSizeEvent: ChartSizeEvent = {
|
||||
name: 'chartSize',
|
||||
data: {
|
||||
maxDimensions: {
|
||||
x: { value: 100, unit: 'percentage' },
|
||||
y: { value: 100, unit: 'percentage' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
handlers.event(chartSizeEvent);
|
||||
|
||||
const timeZone = getTimeZone(getUISettings());
|
||||
const { HeatmapComponent } = await import('../components/heatmap_component');
|
||||
const { isInteractive } = handlers;
|
||||
|
|
|
@ -21,7 +21,11 @@ import {
|
|||
import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils';
|
||||
import { Datatable } from '@kbn/expressions-plugin/common';
|
||||
import { StartServicesGetter } from '@kbn/kibana-utils-plugin/public';
|
||||
import { extractContainerType, extractVisualizationType } from '@kbn/chart-expressions-common';
|
||||
import {
|
||||
ChartSizeEvent,
|
||||
extractContainerType,
|
||||
extractVisualizationType,
|
||||
} from '@kbn/chart-expressions-common';
|
||||
import { ExpressionLegacyMetricPluginStart } from '../plugin';
|
||||
import { EXPRESSION_METRIC_NAME, MetricVisRenderConfig, VisParams } from '../../common';
|
||||
|
||||
|
@ -92,6 +96,18 @@ export const getMetricVisRenderer: (
|
|||
handlers.done();
|
||||
};
|
||||
|
||||
const chartSizeEvent: ChartSizeEvent = {
|
||||
name: 'chartSize',
|
||||
data: {
|
||||
maxDimensions: {
|
||||
x: { value: 100, unit: 'percentage' },
|
||||
y: { value: 100, unit: 'percentage' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
handlers.event(chartSizeEvent);
|
||||
|
||||
render(
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
<VisualizationContainer
|
||||
|
|
|
@ -22,7 +22,6 @@ import {
|
|||
import { SerializedFieldFormat } from '@kbn/field-formats-plugin/common';
|
||||
import { SerializableRecord } from '@kbn/utility-types';
|
||||
import type { IUiSettingsClient } from '@kbn/core/public';
|
||||
import { HtmlAttributes } from 'csstype';
|
||||
import { CustomPaletteState } from '@kbn/charts-plugin/common/expressions/palette/types';
|
||||
import { DimensionsVisParam } from '../../common';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
|
@ -208,7 +207,7 @@ const defaultProps = {
|
|||
filterable: true,
|
||||
renderMode: 'view',
|
||||
uiSettings: {} as unknown as IUiSettingsClient,
|
||||
} as Pick<MetricVisComponentProps, 'renderComplete' | 'fireEvent' | 'filterable' | 'renderMode'>;
|
||||
} as Pick<MetricVisComponentProps, 'renderComplete' | 'fireEvent' | 'filterable'>;
|
||||
|
||||
describe('MetricVisComponent', function () {
|
||||
afterEach(() => {
|
||||
|
@ -239,7 +238,7 @@ describe('MetricVisComponent', function () {
|
|||
|
||||
expect(visConfig).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": [Function],
|
||||
"subtitle": undefined,
|
||||
|
@ -299,7 +298,7 @@ describe('MetricVisComponent', function () {
|
|||
|
||||
expect(configWithPrefix).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span>
|
||||
secondary prefix
|
||||
number-13.6328125
|
||||
|
@ -348,7 +347,7 @@ describe('MetricVisComponent', function () {
|
|||
|
||||
expect(configWithProgress).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"domainMax": 28.984375,
|
||||
"extra": <span />,
|
||||
"icon": [Function],
|
||||
|
@ -425,7 +424,7 @@ describe('MetricVisComponent', function () {
|
|||
expect(visConfig).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -434,7 +433,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -443,7 +442,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -452,7 +451,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -461,7 +460,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -590,7 +589,7 @@ describe('MetricVisComponent', function () {
|
|||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -599,7 +598,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -608,7 +607,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -617,7 +616,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -626,7 +625,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -637,7 +636,7 @@ describe('MetricVisComponent', function () {
|
|||
],
|
||||
Array [
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
"subtitle": "Median products.base_price",
|
||||
|
@ -678,7 +677,7 @@ describe('MetricVisComponent', function () {
|
|||
Array [
|
||||
Array [
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"domainMax": 28.984375,
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
|
@ -689,7 +688,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"domainMax": 28.984375,
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
|
@ -700,7 +699,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"domainMax": 25.984375,
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
|
@ -711,7 +710,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"domainMax": 25.784375,
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
|
@ -722,7 +721,7 @@ describe('MetricVisComponent', function () {
|
|||
"valueFormatter": [Function],
|
||||
},
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"domainMax": 25.348011363636363,
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
|
@ -735,7 +734,7 @@ describe('MetricVisComponent', function () {
|
|||
],
|
||||
Array [
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"domainMax": 24.984375,
|
||||
"extra": <span />,
|
||||
"icon": undefined,
|
||||
|
@ -849,124 +848,60 @@ describe('MetricVisComponent', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('rendering with no data', () => {});
|
||||
|
||||
it('should constrain dimensions in edit mode', () => {
|
||||
const getContainerStyles = (editMode: boolean, multipleTiles: boolean) =>
|
||||
(
|
||||
shallow(
|
||||
<MetricVis
|
||||
data={table}
|
||||
config={{
|
||||
metric: {
|
||||
progressDirection: 'vertical',
|
||||
maxCols: 5,
|
||||
},
|
||||
dimensions: {
|
||||
metric: basePriceColumnId,
|
||||
breakdownBy: multipleTiles ? dayOfWeekColumnId : undefined,
|
||||
},
|
||||
}}
|
||||
{...defaultProps}
|
||||
renderMode={editMode ? 'edit' : 'view'}
|
||||
/>
|
||||
)
|
||||
.find('div')
|
||||
.at(0)
|
||||
.props() as HtmlAttributes & { css: { styles: string } }
|
||||
).css.styles;
|
||||
const getDimensionsRequest = (multipleTiles: boolean) => {
|
||||
const fireEvent = jest.fn();
|
||||
const wrapper = shallow(
|
||||
<MetricVis
|
||||
data={table}
|
||||
config={{
|
||||
metric: {
|
||||
progressDirection: 'vertical',
|
||||
maxCols: 5,
|
||||
},
|
||||
dimensions: {
|
||||
metric: basePriceColumnId,
|
||||
breakdownBy: multipleTiles ? dayOfWeekColumnId : undefined,
|
||||
},
|
||||
}}
|
||||
{...defaultProps}
|
||||
fireEvent={fireEvent}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(getContainerStyles(false, false)).toMatchInlineSnapshot(`
|
||||
"
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
wrapper.find(Settings).props().onWillRender!();
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
inline-size: 16px;
|
||||
block-size: 16px;
|
||||
}
|
||||
return fireEvent.mock.calls[0][0].data;
|
||||
};
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(105,112,125,0.5);
|
||||
background-clip: content-box;
|
||||
border-radius: 16px;
|
||||
border: calc(8px * 0.75) solid transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner,
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
scrollbar-color: rgba(105,112,125,0.5) transparent;
|
||||
|
||||
"
|
||||
expect(getDimensionsRequest(false)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"maxDimensions": Object {
|
||||
"x": Object {
|
||||
"unit": "pixels",
|
||||
"value": 300,
|
||||
},
|
||||
"y": Object {
|
||||
"unit": "pixels",
|
||||
"value": 300,
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
expect(getContainerStyles(true, false)).toMatchInlineSnapshot(`
|
||||
"
|
||||
height: 300px;
|
||||
width: 300px;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
inline-size: 16px;
|
||||
block-size: 16px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(105,112,125,0.5);
|
||||
background-clip: content-box;
|
||||
border-radius: 16px;
|
||||
border: calc(8px * 0.75) solid transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner,
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
scrollbar-color: rgba(105,112,125,0.5) transparent;
|
||||
|
||||
"
|
||||
`);
|
||||
|
||||
expect(getContainerStyles(true, true)).toMatchInlineSnapshot(`
|
||||
"
|
||||
height: 400px;
|
||||
width: 1000px;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
inline-size: 16px;
|
||||
block-size: 16px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(105,112,125,0.5);
|
||||
background-clip: content-box;
|
||||
border-radius: 16px;
|
||||
border: calc(8px * 0.75) solid transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner,
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
scrollbar-color: rgba(105,112,125,0.5) transparent;
|
||||
|
||||
"
|
||||
expect(getDimensionsRequest(true)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"maxDimensions": Object {
|
||||
"x": Object {
|
||||
"unit": "pixels",
|
||||
"value": 1000,
|
||||
},
|
||||
"y": Object {
|
||||
"unit": "pixels",
|
||||
"value": 400,
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
|
@ -1308,7 +1243,7 @@ describe('MetricVisComponent', function () {
|
|||
|
||||
const [[datum]] = component.find(Metric).props().data!;
|
||||
|
||||
expect(datum!.color).toBe(euiThemeVars.euiColorLightestShade);
|
||||
expect(datum!.color).toBe(euiThemeVars.euiColorEmptyShade);
|
||||
expect(mockGetColorForValue).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -29,7 +29,6 @@ import type {
|
|||
DatatableColumn,
|
||||
DatatableRow,
|
||||
IInterpreterRenderHandlers,
|
||||
RenderMode,
|
||||
} from '@kbn/expressions-plugin/common';
|
||||
import { CustomPaletteState } from '@kbn/charts-plugin/public';
|
||||
import {
|
||||
|
@ -41,13 +40,13 @@ import { css } from '@emotion/react';
|
|||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
import { useResizeObserver, useEuiScrollBar, EuiIcon } from '@elastic/eui';
|
||||
import { AllowedChartOverrides, AllowedSettingsOverrides } from '@kbn/charts-plugin/common';
|
||||
import { getOverridesFor } from '@kbn/chart-expressions-common';
|
||||
import { type ChartSizeEvent, getOverridesFor } from '@kbn/chart-expressions-common';
|
||||
import { DEFAULT_TRENDLINE_NAME } from '../../common/constants';
|
||||
import { VisParams } from '../../common';
|
||||
import { getPaletteService, getThemeService, getFormatService } from '../services';
|
||||
import { getDataBoundsForPalette } from '../utils';
|
||||
|
||||
export const defaultColor = euiThemeVars.euiColorLightestShade;
|
||||
export const defaultColor = euiThemeVars.euiColorEmptyShade;
|
||||
|
||||
function enhanceFieldFormat(serializedFieldFormat: SerializedFieldFormat | undefined) {
|
||||
const formatId = serializedFieldFormat?.id || 'number';
|
||||
|
@ -140,7 +139,6 @@ export interface MetricVisComponentProps {
|
|||
config: Pick<VisParams, 'metric' | 'dimensions'>;
|
||||
renderComplete: IInterpreterRenderHandlers['done'];
|
||||
fireEvent: IInterpreterRenderHandlers['event'];
|
||||
renderMode: RenderMode;
|
||||
filterable: boolean;
|
||||
overrides?: AllowedSettingsOverrides & AllowedChartOverrides;
|
||||
}
|
||||
|
@ -150,10 +148,11 @@ export const MetricVis = ({
|
|||
config,
|
||||
renderComplete,
|
||||
fireEvent,
|
||||
renderMode,
|
||||
filterable,
|
||||
overrides,
|
||||
}: MetricVisComponentProps) => {
|
||||
const grid = useRef<MetricSpec['data']>([[]]);
|
||||
|
||||
const onRenderChange = useCallback<RenderChangeListener>(
|
||||
(isRendered) => {
|
||||
if (isRendered) {
|
||||
|
@ -163,6 +162,20 @@ export const MetricVis = ({
|
|||
[renderComplete]
|
||||
);
|
||||
|
||||
const onWillRender = useCallback(() => {
|
||||
const maxTileSideLength = grid.current.length * grid.current[0].length > 1 ? 200 : 300;
|
||||
const event: ChartSizeEvent = {
|
||||
name: 'chartSize',
|
||||
data: {
|
||||
maxDimensions: {
|
||||
y: { value: grid.current.length * maxTileSideLength, unit: 'pixels' },
|
||||
x: { value: grid.current[0]?.length * maxTileSideLength, unit: 'pixels' },
|
||||
},
|
||||
},
|
||||
};
|
||||
fireEvent(event);
|
||||
}, [fireEvent, grid]);
|
||||
|
||||
const [scrollChildHeight, setScrollChildHeight] = useState<string>('100%');
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
const scrollDimensions = useResizeObserver(scrollContainerRef.current);
|
||||
|
@ -289,28 +302,19 @@ export const MetricVis = ({
|
|||
'settings'
|
||||
) as Partial<SettingsProps>;
|
||||
|
||||
const grid: MetricSpec['data'] = [];
|
||||
const newGrid: MetricSpec['data'] = [];
|
||||
for (let i = 0; i < metricConfigs.length; i += maxCols) {
|
||||
grid.push(metricConfigs.slice(i, i + maxCols));
|
||||
newGrid.push(metricConfigs.slice(i, i + maxCols));
|
||||
}
|
||||
|
||||
let pixelHeight;
|
||||
let pixelWidth;
|
||||
if (renderMode === 'edit') {
|
||||
// In the editor, we constrain the maximum size of the tiles for aesthetic reasons
|
||||
const maxTileSideLength = metricConfigs.flat().length > 1 ? 200 : 300;
|
||||
pixelHeight = grid.length * maxTileSideLength;
|
||||
pixelWidth = grid[0]?.length * maxTileSideLength;
|
||||
}
|
||||
grid.current = newGrid;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
css={css`
|
||||
height: ${pixelHeight ? `${pixelHeight}px` : '100%'};
|
||||
width: ${pixelWidth ? `${pixelWidth}px` : '100%'};
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
${useEuiScrollBar()}
|
||||
`}
|
||||
|
@ -322,6 +326,7 @@ export const MetricVis = ({
|
|||
>
|
||||
<Chart {...getOverridesFor(overrides, 'chart')}>
|
||||
<Settings
|
||||
onWillRender={onWillRender}
|
||||
locale={i18n.getLocale()}
|
||||
theme={[
|
||||
{
|
||||
|
@ -342,7 +347,7 @@ export const MetricVis = ({
|
|||
filterable
|
||||
? (events) => {
|
||||
const colRef = breakdownByColumn ?? primaryMetricColumn;
|
||||
const rowLength = grid[0].length;
|
||||
const rowLength = grid.current[0].length;
|
||||
events.forEach((event) => {
|
||||
if (isMetricElementEvent(event)) {
|
||||
const colIdx = data.columns.findIndex((col) => col === colRef);
|
||||
|
@ -360,7 +365,7 @@ export const MetricVis = ({
|
|||
}
|
||||
{...settingsOverrides}
|
||||
/>
|
||||
<Metric id="metric" data={grid} />
|
||||
<Metric id="metric" data={grid.current} />
|
||||
</Chart>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -101,7 +101,6 @@ export const getMetricVisRenderer = (
|
|||
config={visConfig}
|
||||
renderComplete={renderComplete}
|
||||
fireEvent={handlers.event}
|
||||
renderMode={handlers.getRenderMode()}
|
||||
filterable={filterable}
|
||||
overrides={overrides}
|
||||
/>
|
||||
|
|
|
@ -22,6 +22,7 @@ import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
|||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils';
|
||||
import {
|
||||
type ChartSizeEvent,
|
||||
extractContainerType,
|
||||
extractVisualizationType,
|
||||
isOnAggBasedEditor,
|
||||
|
@ -116,6 +117,18 @@ export const getPartitionVisRenderer: (
|
|||
|
||||
const hasOpenedOnAggBasedEditor = isOnAggBasedEditor(handlers.getExecutionContext());
|
||||
|
||||
const chartSizeEvent: ChartSizeEvent = {
|
||||
name: 'chartSize',
|
||||
data: {
|
||||
maxDimensions: {
|
||||
x: { value: 100, unit: 'percentage' },
|
||||
y: { value: 100, unit: 'percentage' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
handlers.event(chartSizeEvent);
|
||||
|
||||
render(
|
||||
<I18nProvider>
|
||||
<KibanaThemeProvider theme$={core.theme.theme$}>
|
||||
|
|
|
@ -15,7 +15,11 @@ import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
|||
import { VisualizationContainer } from '@kbn/visualizations-plugin/public';
|
||||
import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers';
|
||||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { extractContainerType, extractVisualizationType } from '@kbn/chart-expressions-common';
|
||||
import {
|
||||
type ChartSizeEvent,
|
||||
extractContainerType,
|
||||
extractVisualizationType,
|
||||
} from '@kbn/chart-expressions-common';
|
||||
|
||||
import { ExpressionTagcloudRendererDependencies } from '../plugin';
|
||||
import { TagcloudRendererConfig } from '../../common/types';
|
||||
|
@ -66,6 +70,18 @@ export const tagcloudRenderer: (
|
|||
handlers.done();
|
||||
};
|
||||
|
||||
const chartSizeEvent: ChartSizeEvent = {
|
||||
name: 'chartSize',
|
||||
data: {
|
||||
maxDimensions: {
|
||||
x: { value: 100, unit: 'percentage' },
|
||||
y: { value: 100, unit: 'percentage' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
handlers.event(chartSizeEvent);
|
||||
|
||||
const palettesRegistry = await plugins.charts.palettes.getPalettes();
|
||||
let isDarkMode = false;
|
||||
plugins.charts.theme.darkModeEnabled$
|
||||
|
|
|
@ -300,6 +300,18 @@ exports[`XYChart component it renders area 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -1129,6 +1141,7 @@ exports[`XYChart component it renders area 1`] = `
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={0}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -1836,6 +1849,18 @@ exports[`XYChart component it renders bar 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -2665,6 +2690,7 @@ exports[`XYChart component it renders bar 1`] = `
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={0}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -3372,6 +3398,18 @@ exports[`XYChart component it renders horizontal bar 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -4201,6 +4239,7 @@ exports[`XYChart component it renders horizontal bar 1`] = `
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={90}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -4908,6 +4947,18 @@ exports[`XYChart component it renders line 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -5737,6 +5788,7 @@ exports[`XYChart component it renders line 1`] = `
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={0}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -6444,6 +6496,18 @@ exports[`XYChart component it renders stacked area 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -7273,6 +7337,7 @@ exports[`XYChart component it renders stacked area 1`] = `
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={0}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -7980,6 +8045,18 @@ exports[`XYChart component it renders stacked bar 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -8809,6 +8886,7 @@ exports[`XYChart component it renders stacked bar 1`] = `
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={0}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -9516,6 +9594,18 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -10345,6 +10435,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = `
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={90}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -11052,6 +11143,18 @@ exports[`XYChart component split chart should render split chart if both, splitR
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -11911,6 +12014,7 @@ exports[`XYChart component split chart should render split chart if both, splitR
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={0}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -12832,6 +12936,18 @@ exports[`XYChart component split chart should render split chart if splitColumnA
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -13685,6 +13801,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={0}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
@ -14599,6 +14716,18 @@ exports[`XYChart component split chart should render split chart if splitRowAcce
|
|||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
css={
|
||||
Object {
|
||||
"backgroundColor": "#ffffff",
|
||||
"display": "none",
|
||||
"height": "100%",
|
||||
"position": "absolute",
|
||||
"width": "100%",
|
||||
"zIndex": 1,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ContextProvider
|
||||
value={
|
||||
Object {
|
||||
|
@ -15452,6 +15581,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce
|
|||
onElementClick={[Function]}
|
||||
onPointerUpdate={[Function]}
|
||||
onRenderChange={[Function]}
|
||||
onResize={[Function]}
|
||||
rotation={0}
|
||||
showLegend={false}
|
||||
showLegendExtra={false}
|
||||
|
|
|
@ -129,6 +129,8 @@ describe('XYChart component', () => {
|
|||
eventAnnotationService: eventAnnotationServiceMock,
|
||||
renderComplete: jest.fn(),
|
||||
timeFormat: 'MMM D, YYYY @ HH:mm:ss.SSS',
|
||||
setChartSize: jest.fn(),
|
||||
shouldUseVeil: false,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -52,7 +52,11 @@ import {
|
|||
LegendSizeToPixels,
|
||||
} from '@kbn/visualizations-plugin/common/constants';
|
||||
import { PersistedState } from '@kbn/visualizations-plugin/public';
|
||||
import { getOverridesFor } from '@kbn/chart-expressions-common';
|
||||
import {
|
||||
useSizeTransitionVeil,
|
||||
getOverridesFor,
|
||||
ChartSizeSpec,
|
||||
} from '@kbn/chart-expressions-common';
|
||||
import type {
|
||||
FilterEvent,
|
||||
BrushEvent,
|
||||
|
@ -144,8 +148,11 @@ export type XYChartRenderProps = Omit<XYChartProps, 'canNavigateToLens'> & {
|
|||
syncCursor: boolean;
|
||||
eventAnnotationService: EventAnnotationServiceType;
|
||||
renderComplete: () => void;
|
||||
shouldUseVeil: boolean;
|
||||
uiState?: PersistedState;
|
||||
timeFormat: string;
|
||||
setChartSize: (chartSizeSpec: ChartSizeSpec) => void;
|
||||
shouldShowLegendAction?: (actionId: string) => boolean;
|
||||
};
|
||||
|
||||
function nonNullable<T>(v: T): v is NonNullable<T> {
|
||||
|
@ -199,12 +206,16 @@ export function XYChart({
|
|||
onClickMultiValue,
|
||||
layerCellValueActions,
|
||||
onSelectRange,
|
||||
setChartSize,
|
||||
interactive = true,
|
||||
syncColors,
|
||||
syncTooltips,
|
||||
syncCursor,
|
||||
shouldUseVeil,
|
||||
|
||||
useLegacyTimeAxis,
|
||||
renderComplete,
|
||||
|
||||
uiState,
|
||||
timeFormat,
|
||||
overrides,
|
||||
|
@ -293,6 +304,34 @@ export function XYChart({
|
|||
);
|
||||
|
||||
const dataLayers: CommonXYDataLayerConfig[] = filteredLayers.filter(isDataLayer);
|
||||
|
||||
const isTimeViz = isTimeChart(dataLayers);
|
||||
|
||||
const chartSizeSpec: ChartSizeSpec =
|
||||
isTimeViz && !isHorizontalChart(dataLayers)
|
||||
? {
|
||||
aspectRatio: {
|
||||
x: 16,
|
||||
y: 9,
|
||||
},
|
||||
minDimensions: {
|
||||
y: { value: 300, unit: 'pixels' },
|
||||
x: { value: 100, unit: 'percentage' },
|
||||
},
|
||||
}
|
||||
: {
|
||||
maxDimensions: {
|
||||
x: { value: 100, unit: 'percentage' },
|
||||
y: { value: 100, unit: 'percentage' },
|
||||
},
|
||||
};
|
||||
|
||||
const { veil, onResize, containerRef } = useSizeTransitionVeil(
|
||||
chartSizeSpec,
|
||||
setChartSize,
|
||||
shouldUseVeil
|
||||
);
|
||||
|
||||
const formattedDatatables = useMemo(
|
||||
() =>
|
||||
getFormattedTablesByLayers(dataLayers, formatFactory, splitColumnAccessor, splitRowAccessor),
|
||||
|
@ -371,8 +410,6 @@ export function XYChart({
|
|||
(layer) => isDataLayer(layer) && layer.splitAccessors && layer.splitAccessors.length
|
||||
);
|
||||
|
||||
const isTimeViz = isTimeChart(dataLayers);
|
||||
|
||||
const defaultXScaleType = isTimeViz ? XScaleTypes.TIME : XScaleTypes.ORDINAL;
|
||||
|
||||
const isHistogramViz = dataLayers.every((l) => l.isHistogram);
|
||||
|
@ -711,7 +748,8 @@ export function XYChart({
|
|||
);
|
||||
|
||||
return (
|
||||
<div css={chartContainerStyle}>
|
||||
<div ref={containerRef} css={chartContainerStyle}>
|
||||
{veil}
|
||||
{showLegend !== undefined && uiState && (
|
||||
<LegendToggle
|
||||
onClick={toggleLegend}
|
||||
|
@ -785,6 +823,7 @@ export function XYChart({
|
|||
/>
|
||||
}
|
||||
onRenderChange={onRenderChange}
|
||||
onResize={onResize}
|
||||
onPointerUpdate={syncCursor ? handleCursorUpdate : undefined}
|
||||
externalPointerEvents={{
|
||||
tooltip: { visible: syncTooltips, placement: Placement.Right },
|
||||
|
|
|
@ -26,7 +26,12 @@ import { FormatFactory } from '@kbn/field-formats-plugin/common';
|
|||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { UsageCollectionStart } from '@kbn/usage-collection-plugin/public';
|
||||
import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils';
|
||||
import { extractContainerType, extractVisualizationType } from '@kbn/chart-expressions-common';
|
||||
import {
|
||||
type ChartSizeEvent,
|
||||
type ChartSizeSpec,
|
||||
extractContainerType,
|
||||
extractVisualizationType,
|
||||
} from '@kbn/chart-expressions-common';
|
||||
|
||||
import type { getDataLayers } from '../helpers';
|
||||
import { LayerTypes, SeriesTypes } from '../../common/constants';
|
||||
|
@ -215,6 +220,10 @@ export const getXyChartRenderer = ({
|
|||
const onClickMultiValue = (data: MultiFilterEvent['data']) => {
|
||||
handlers.event({ name: 'multiFilter', data });
|
||||
};
|
||||
const setChartSize = (data: ChartSizeSpec) => {
|
||||
const event: ChartSizeEvent = { name: 'chartSize', data };
|
||||
handlers.event(event);
|
||||
};
|
||||
|
||||
const layerCellValueActions = await getLayerCellValueActions(
|
||||
getDataLayers(config.args.layers),
|
||||
|
@ -275,8 +284,10 @@ export const getXyChartRenderer = ({
|
|||
syncColors={config.syncColors}
|
||||
syncTooltips={config.syncTooltips}
|
||||
syncCursor={config.syncCursor}
|
||||
shouldUseVeil={handlers.shouldUseSizeTransitionVeil()}
|
||||
uiState={handlers.uiState as PersistedState}
|
||||
renderComplete={renderComplete}
|
||||
setChartSize={setChartSize}
|
||||
/>
|
||||
</div>
|
||||
</I18nProvider>
|
||||
|
|
|
@ -287,6 +287,7 @@ export class Execution<
|
|||
isSyncColorsEnabled: () => execution.params.syncColors!,
|
||||
isSyncCursorEnabled: () => execution.params.syncCursor!,
|
||||
isSyncTooltipsEnabled: () => execution.params.syncTooltips!,
|
||||
shouldUseSizeTransitionVeil: () => execution.params.shouldUseSizeTransitionVeil!,
|
||||
...execution.executor.context,
|
||||
getExecutionContext: () => execution.params.executionContext,
|
||||
};
|
||||
|
|
|
@ -72,6 +72,11 @@ export interface ExecutionContext<InspectorAdapters extends Adapters = Adapters>
|
|||
*/
|
||||
isSyncTooltipsEnabled?: () => boolean;
|
||||
|
||||
/**
|
||||
* Returns whether or not to use the size transition veil when resizing visualizations.
|
||||
*/
|
||||
shouldUseSizeTransitionVeil?: () => boolean;
|
||||
|
||||
/**
|
||||
* Contains the meta-data about the source of the expression.
|
||||
*/
|
||||
|
|
|
@ -97,6 +97,9 @@ export interface IInterpreterRenderHandlers {
|
|||
isSyncCursorEnabled(): boolean;
|
||||
|
||||
isSyncTooltipsEnabled(): boolean;
|
||||
|
||||
shouldUseSizeTransitionVeil(): boolean;
|
||||
|
||||
/**
|
||||
* This uiState interface is actually `PersistedState` from the visualizations plugin,
|
||||
* but expressions cannot know about vis or it creates a mess of circular dependencies.
|
||||
|
|
|
@ -156,6 +156,11 @@ export interface ExpressionExecutionParams {
|
|||
|
||||
syncTooltips?: boolean;
|
||||
|
||||
// if this is set to true, a veil will be shown when resizing visualizations in response
|
||||
// to a chart resize event (see src/plugins/chart_expressions/common/chart_size_transition_veil.tsx).
|
||||
// This should be only set to true if the client will be responding to the resize events
|
||||
shouldUseSizeTransitionVeil?: boolean;
|
||||
|
||||
inspectorAdapters?: Adapters;
|
||||
|
||||
executionContext?: KibanaExecutionContext;
|
||||
|
|
|
@ -60,6 +60,7 @@ export class ExpressionLoader {
|
|||
syncColors: params?.syncColors,
|
||||
syncTooltips: params?.syncTooltips,
|
||||
syncCursor: params?.syncCursor,
|
||||
shouldUseSizeTransitionVeil: params?.shouldUseSizeTransitionVeil,
|
||||
hasCompatibleActions: params?.hasCompatibleActions,
|
||||
getCompatibleCellValueActions: params?.getCompatibleCellValueActions,
|
||||
executionContext: params?.executionContext,
|
||||
|
@ -148,6 +149,7 @@ export class ExpressionLoader {
|
|||
syncColors: params.syncColors,
|
||||
syncCursor: params?.syncCursor,
|
||||
syncTooltips: params.syncTooltips,
|
||||
shouldUseSizeTransitionVeil: params.shouldUseSizeTransitionVeil,
|
||||
executionContext: params.executionContext,
|
||||
partial: params.partial,
|
||||
throttle: params.throttle,
|
||||
|
|
|
@ -33,6 +33,7 @@ export interface ExpressionRenderHandlerParams {
|
|||
syncCursor?: boolean;
|
||||
syncTooltips?: boolean;
|
||||
interactive?: boolean;
|
||||
shouldUseSizeTransitionVeil?: boolean;
|
||||
hasCompatibleActions?: (event: ExpressionRendererEvent) => Promise<boolean>;
|
||||
getCompatibleCellValueActions?: (data: object[]) => Promise<unknown[]>;
|
||||
executionContext?: KibanaExecutionContext;
|
||||
|
@ -62,6 +63,7 @@ export class ExpressionRenderHandler {
|
|||
syncColors,
|
||||
syncTooltips,
|
||||
syncCursor,
|
||||
shouldUseSizeTransitionVeil,
|
||||
interactive,
|
||||
hasCompatibleActions = async () => false,
|
||||
getCompatibleCellValueActions = async () => [],
|
||||
|
@ -113,6 +115,9 @@ export class ExpressionRenderHandler {
|
|||
isSyncCursorEnabled: () => {
|
||||
return syncCursor || true;
|
||||
},
|
||||
shouldUseSizeTransitionVeil: () => {
|
||||
return Boolean(shouldUseSizeTransitionVeil);
|
||||
},
|
||||
isInteractive: () => {
|
||||
return interactive ?? true;
|
||||
},
|
||||
|
|
|
@ -52,6 +52,10 @@ export interface IExpressionLoaderParams {
|
|||
syncColors?: boolean;
|
||||
syncCursor?: boolean;
|
||||
syncTooltips?: boolean;
|
||||
// if this is set to true, a veil will be shown when resizing visualizations in response
|
||||
// to a chart resize event (see src/plugins/chart_expressions/common/chart_size_transition_veil.tsx).
|
||||
// This should be only set to true if the client will be responding to the resize events
|
||||
shouldUseSizeTransitionVeil?: boolean;
|
||||
hasCompatibleActions?: ExpressionRenderHandlerParams['hasCompatibleActions'];
|
||||
getCompatibleCellValueActions?: ExpressionRenderHandlerParams['getCompatibleCellValueActions'];
|
||||
executionContext?: KibanaExecutionContext;
|
||||
|
|
|
@ -18,6 +18,7 @@ export const defaultHandlers: IInterpreterRenderHandlers = {
|
|||
isSyncColorsEnabled: () => false,
|
||||
isSyncCursorEnabled: () => true,
|
||||
isSyncTooltipsEnabled: () => false,
|
||||
shouldUseSizeTransitionVeil: () => false,
|
||||
isInteractive: () => true,
|
||||
getExecutionContext: () => undefined,
|
||||
done: action('done'),
|
||||
|
|
|
@ -40,6 +40,7 @@ import {
|
|||
import type { RenderMode } from '@kbn/expressions-plugin/common';
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '@kbn/data-views-plugin/public';
|
||||
import { mapAndFlattenFilters } from '@kbn/data-plugin/public';
|
||||
import { isChartSizeEvent } from '@kbn/chart-expressions-common';
|
||||
import { isFallbackDataView } from '../visualize_app/utils';
|
||||
import { VisualizationMissedSavedObjectError } from '../components/visualization_missed_saved_object_error';
|
||||
import VisualizationError from '../components/visualization_error';
|
||||
|
@ -477,6 +478,10 @@ export class VisualizeEmbeddable
|
|||
this.handler.events$
|
||||
.pipe(
|
||||
mergeMap(async (event) => {
|
||||
// Visualize doesn't respond to sizing events, so ignore.
|
||||
if (isChartSizeEvent(event)) {
|
||||
return;
|
||||
}
|
||||
if (!this.input.disableTriggers) {
|
||||
const triggerId = get(VIS_EVENT_TO_TRIGGER, event.name, VIS_EVENT_TO_TRIGGER.filter);
|
||||
let context;
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
"@kbn/search-response-warnings",
|
||||
"@kbn/logging",
|
||||
"@kbn/content-management-table-list-view-common",
|
||||
"@kbn/chart-expressions-common",
|
||||
"@kbn/shared-ux-utility"
|
||||
],
|
||||
"exclude": [
|
||||
|
|
|
@ -17,6 +17,7 @@ export const defaultHandlers: RendererHandlers = {
|
|||
isSyncColorsEnabled: () => false,
|
||||
isSyncCursorEnabled: () => true,
|
||||
isSyncTooltipsEnabled: () => false,
|
||||
shouldUseSizeTransitionVeil: () => false,
|
||||
isInteractive: () => true,
|
||||
onComplete: (fn) => undefined,
|
||||
onEmbeddableDestroyed: action('onEmbeddableDestroyed'),
|
||||
|
|
|
@ -29,6 +29,7 @@ export const createBaseHandlers = (): IInterpreterRenderHandlers => ({
|
|||
isSyncColorsEnabled: () => false,
|
||||
isSyncTooltipsEnabled: () => false,
|
||||
isSyncCursorEnabled: () => true,
|
||||
shouldUseSizeTransitionVeil: () => false,
|
||||
isInteractive: () => true,
|
||||
getExecutionContext: () => undefined,
|
||||
});
|
||||
|
|
|
@ -27,6 +27,7 @@ import type { Datatable } from '@kbn/expressions-plugin/public';
|
|||
import { DropIllustration } from '@kbn/chart-icons';
|
||||
import { DragDrop, useDragDropContext, DragDropIdentifier } from '@kbn/dom-drag-drop';
|
||||
import { reportPerformanceMetricEvent } from '@kbn/ebt-tools';
|
||||
import { ChartSizeSpec, isChartSizeEvent } from '@kbn/chart-expressions-common';
|
||||
import { trackUiCounterEvents } from '../../../lens_ui_telemetry';
|
||||
import { getSearchWarningMessages } from '../../../utils';
|
||||
import {
|
||||
|
@ -43,6 +44,7 @@ import {
|
|||
UserMessagesGetter,
|
||||
AddUserMessages,
|
||||
isMessageRemovable,
|
||||
VisualizationDisplayOptions,
|
||||
} from '../../../types';
|
||||
import { switchToSuggestion } from '../suggestion_helpers';
|
||||
import { buildExpression } from '../expression_helpers';
|
||||
|
@ -413,6 +415,8 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
}
|
||||
}, [expressionExists, localState.expressionToRender]);
|
||||
|
||||
const [chartSizeSpec, setChartSize] = useState<ChartSizeSpec | undefined>();
|
||||
|
||||
const onEvent = useCallback(
|
||||
(event: ExpressionRendererEvent) => {
|
||||
if (!plugins.uiActions) {
|
||||
|
@ -443,10 +447,15 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (isChartSizeEvent(event)) {
|
||||
setChartSize(event.data);
|
||||
}
|
||||
},
|
||||
[plugins.data.datatableUtilities, plugins.uiActions, activeVisualization, dispatchLens]
|
||||
);
|
||||
|
||||
const displayOptions = activeVisualization?.getDisplayOptions?.();
|
||||
const hasCompatibleActions = useCallback(
|
||||
async (event: ExpressionRendererEvent) => {
|
||||
if (!plugins.uiActions) {
|
||||
|
@ -478,6 +487,10 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
const IS_DARK_THEME: boolean = useObservable(core.theme.theme$, { darkMode: false }).darkMode;
|
||||
|
||||
const renderDragDropPrompt = () => {
|
||||
if (chartSizeSpec) {
|
||||
setChartSize(undefined);
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiText
|
||||
className={classNames('lnsWorkspacePanel__emptyContent')}
|
||||
|
@ -532,6 +545,10 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
};
|
||||
|
||||
const renderApplyChangesPrompt = () => {
|
||||
if (chartSizeSpec) {
|
||||
setChartSize(undefined);
|
||||
}
|
||||
|
||||
const applyChangesString = i18n.translate('xpack.lens.editorFrame.applyChanges', {
|
||||
defaultMessage: 'Apply changes',
|
||||
});
|
||||
|
@ -590,6 +607,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
onComponentRendered={() => {
|
||||
visualizationRenderStartTime.current = performance.now();
|
||||
}}
|
||||
displayOptions={displayOptions}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -639,7 +657,6 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
return (
|
||||
<WorkspacePanelWrapper
|
||||
framePublicAPI={framePublicAPI}
|
||||
visualizationState={visualization.state}
|
||||
visualizationId={visualization.activeId}
|
||||
datasourceStates={datasourceStates}
|
||||
datasourceMap={datasourceMap}
|
||||
|
@ -647,6 +664,7 @@ export const InnerWorkspacePanel = React.memo(function InnerWorkspacePanel({
|
|||
isFullscreen={isFullscreen}
|
||||
lensInspector={lensInspector}
|
||||
getUserMessages={getUserMessages}
|
||||
displayOptions={chartSizeSpec}
|
||||
>
|
||||
{renderWorkspace()}
|
||||
</WorkspacePanelWrapper>
|
||||
|
@ -686,6 +704,7 @@ export const VisualizationWrapper = ({
|
|||
onRender$,
|
||||
onData$,
|
||||
onComponentRendered,
|
||||
displayOptions,
|
||||
}: {
|
||||
expression: string | null | undefined;
|
||||
lensInspector: LensInspector;
|
||||
|
@ -699,6 +718,7 @@ export const VisualizationWrapper = ({
|
|||
onRender$: () => void;
|
||||
onData$: (data: unknown, adapters?: Partial<DefaultInspectorAdapters>) => void;
|
||||
onComponentRendered: () => void;
|
||||
displayOptions: VisualizationDisplayOptions | undefined;
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
onComponentRendered();
|
||||
|
@ -751,7 +771,7 @@ export const VisualizationWrapper = ({
|
|||
>
|
||||
<ExpressionRendererComponent
|
||||
className="lnsExpressionRenderer__component"
|
||||
padding="m"
|
||||
padding={displayOptions?.noPadding ? undefined : 'm'}
|
||||
expression={expression!}
|
||||
searchContext={searchContext}
|
||||
searchSessionId={searchSessionId}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
background: $euiColorEmptyShade;
|
||||
height: 100%;
|
||||
|
||||
> * {
|
||||
&>* {
|
||||
flex: 1 1 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -37,6 +37,7 @@
|
|||
|
||||
&.lnsWorkspacePanelWrapper--fullscreen {
|
||||
margin-bottom: 0;
|
||||
|
||||
.lnsWorkspacePanelWrapper__pageContentBody {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
@ -45,8 +46,6 @@
|
|||
}
|
||||
|
||||
.lnsWorkspacePanel__dragDrop {
|
||||
border: $euiBorderWidthThin solid transparent;
|
||||
|
||||
&.domDragDrop-isDropTarget {
|
||||
p {
|
||||
transition: filter $euiAnimSpeedFast ease-in-out;
|
||||
|
@ -120,9 +119,7 @@
|
|||
// Hard-coded px values OK (@cchaos)
|
||||
// sass-lint:disable-block indentation
|
||||
filter:
|
||||
drop-shadow(0 6px 12px transparentize($euiShadowColor, .8))
|
||||
drop-shadow(0 4px 4px transparentize($euiShadowColor, .8))
|
||||
drop-shadow(0 2px 2px transparentize($euiShadowColor, .8));
|
||||
drop-shadow(0 6px 12px transparentize($euiShadowColor, .8)) drop-shadow(0 4px 4px transparentize($euiShadowColor, .8)) drop-shadow(0 2px 2px transparentize($euiShadowColor, .8));
|
||||
}
|
||||
|
||||
.lnsDropIllustration__adjustFill {
|
||||
|
@ -134,20 +131,51 @@
|
|||
}
|
||||
|
||||
@keyframes lnsWorkspacePanel__illustrationPulseArrow {
|
||||
0% { transform: translateY(0%); }
|
||||
65% { transform: translateY(0%); }
|
||||
72% { transform: translateY(10%); }
|
||||
79% { transform: translateY(7%); }
|
||||
86% { transform: translateY(10%); }
|
||||
95% { transform: translateY(0); }
|
||||
0% {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
65% {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
72% {
|
||||
transform: translateY(10%);
|
||||
}
|
||||
|
||||
79% {
|
||||
transform: translateY(7%);
|
||||
}
|
||||
|
||||
86% {
|
||||
transform: translateY(10%);
|
||||
}
|
||||
|
||||
95% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lnsWorkspacePanel__illustrationPulseContinuous {
|
||||
0% { transform: translateY(10%); }
|
||||
25% { transform: translateY(15%); }
|
||||
50% { transform: translateY(10%); }
|
||||
75% { transform: translateY(15%); }
|
||||
100% { transform: translateY(10%); }
|
||||
0% {
|
||||
transform: translateY(10%);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateY(15%);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(10%);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateY(15%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(10%);
|
||||
}
|
||||
}
|
||||
|
||||
.lnsVisualizationToolbar--fixed {
|
||||
|
@ -155,4 +183,4 @@
|
|||
width: 100%;
|
||||
z-index: 1;
|
||||
background-color: $euiColorLightestShade;
|
||||
}
|
||||
}
|
|
@ -34,7 +34,6 @@ describe('workspace_panel_wrapper', () => {
|
|||
<>
|
||||
<WorkspacePanelWrapper
|
||||
framePublicAPI={mockFrameAPI}
|
||||
visualizationState={{ internalState: 123 }}
|
||||
visualizationId="myVis"
|
||||
visualizationMap={{
|
||||
myVis: { ...mockVisualization, ToolbarComponent: ToolbarComponentMock },
|
||||
|
@ -45,6 +44,7 @@ describe('workspace_panel_wrapper', () => {
|
|||
lensInspector={{} as unknown as LensInspector}
|
||||
getUserMessages={() => []}
|
||||
children={<span />}
|
||||
displayOptions={undefined}
|
||||
{...propsOverrides}
|
||||
/>
|
||||
<SettingsMenu
|
||||
|
|
|
@ -11,6 +11,9 @@ import React, { useCallback } from 'react';
|
|||
import { EuiPageTemplate, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ChartSizeSpec } from '@kbn/chart-expressions-common';
|
||||
import { ChartSizeUnit } from '@kbn/chart-expressions-common/types';
|
||||
import { Interpolation, Theme, css } from '@emotion/react';
|
||||
import {
|
||||
DatasourceMap,
|
||||
FramePublicAPI,
|
||||
|
@ -25,22 +28,20 @@ import {
|
|||
useLensDispatch,
|
||||
updateVisualizationState,
|
||||
DatasourceStates,
|
||||
VisualizationState,
|
||||
useLensSelector,
|
||||
selectChangesApplied,
|
||||
applyChanges,
|
||||
selectAutoApplyEnabled,
|
||||
selectVisualizationState,
|
||||
} from '../../../state_management';
|
||||
import { WorkspaceTitle } from './title';
|
||||
import { LensInspector } from '../../../lens_inspector_service';
|
||||
import { WorkspaceTitle } from './title';
|
||||
|
||||
export const AUTO_APPLY_DISABLED_STORAGE_KEY = 'autoApplyDisabled';
|
||||
|
||||
export interface WorkspacePanelWrapperProps {
|
||||
children: React.ReactNode | React.ReactNode[];
|
||||
framePublicAPI: FramePublicAPI;
|
||||
visualizationState: VisualizationState['state'];
|
||||
visualizationMap: VisualizationMap;
|
||||
visualizationId: string | null;
|
||||
datasourceMap: DatasourceMap;
|
||||
|
@ -48,8 +49,29 @@ export interface WorkspacePanelWrapperProps {
|
|||
isFullscreen: boolean;
|
||||
lensInspector: LensInspector;
|
||||
getUserMessages: UserMessagesGetter;
|
||||
displayOptions: ChartSizeSpec | undefined;
|
||||
}
|
||||
|
||||
const unitToCSSUnit: Record<ChartSizeUnit, string> = {
|
||||
pixels: 'px',
|
||||
percentage: '%',
|
||||
};
|
||||
|
||||
const getAspectRatioStyles = ({ x, y }: { x: number; y: number }) => {
|
||||
return {
|
||||
aspectRatio: `${x}/${y}`,
|
||||
...(y > x
|
||||
? {
|
||||
height: '100%',
|
||||
width: 'auto',
|
||||
}
|
||||
: {
|
||||
height: 'auto',
|
||||
width: '100%',
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export function VisualizationToolbar(props: {
|
||||
activeVisualization: Visualization | null;
|
||||
framePublicAPI: FramePublicAPI;
|
||||
|
@ -98,12 +120,12 @@ export function VisualizationToolbar(props: {
|
|||
export function WorkspacePanelWrapper({
|
||||
children,
|
||||
framePublicAPI,
|
||||
visualizationState,
|
||||
visualizationId,
|
||||
visualizationMap,
|
||||
datasourceMap,
|
||||
isFullscreen,
|
||||
getUserMessages,
|
||||
displayOptions,
|
||||
}: WorkspacePanelWrapperProps) {
|
||||
const dispatchLens = useLensDispatch();
|
||||
|
||||
|
@ -113,6 +135,34 @@ export function WorkspacePanelWrapper({
|
|||
const activeVisualization = visualizationId ? visualizationMap[visualizationId] : null;
|
||||
const userMessages = getUserMessages('toolbar');
|
||||
|
||||
const aspectRatio = displayOptions?.aspectRatio;
|
||||
const maxDimensions = displayOptions?.maxDimensions;
|
||||
const minDimensions = displayOptions?.minDimensions;
|
||||
|
||||
let visDimensionsCSS: Interpolation<Theme> = {};
|
||||
|
||||
if (aspectRatio) {
|
||||
visDimensionsCSS = getAspectRatioStyles(aspectRatio ?? maxDimensions);
|
||||
}
|
||||
|
||||
if (maxDimensions) {
|
||||
visDimensionsCSS.maxWidth = maxDimensions.x
|
||||
? `${maxDimensions.x.value}${unitToCSSUnit[maxDimensions.x.unit]}`
|
||||
: '';
|
||||
visDimensionsCSS.maxHeight = maxDimensions.y
|
||||
? `${maxDimensions.y.value}${unitToCSSUnit[maxDimensions.y.unit]}`
|
||||
: '';
|
||||
}
|
||||
|
||||
if (minDimensions) {
|
||||
visDimensionsCSS.minWidth = minDimensions.x
|
||||
? `${minDimensions.x.value}${unitToCSSUnit[minDimensions.x.unit]}`
|
||||
: '';
|
||||
visDimensionsCSS.minHeight = minDimensions.y
|
||||
? `${minDimensions.y.value}${unitToCSSUnit[minDimensions.y.unit]}`
|
||||
: '';
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiPageTemplate
|
||||
direction="column"
|
||||
|
@ -190,6 +240,7 @@ export function WorkspacePanelWrapper({
|
|||
</EuiFlexGroup>
|
||||
</EuiPageTemplate.Section>
|
||||
)}
|
||||
|
||||
<EuiPageTemplate.Section
|
||||
grow={true}
|
||||
paddingSize="none"
|
||||
|
@ -199,10 +250,32 @@ export function WorkspacePanelWrapper({
|
|||
className={classNames('lnsWorkspacePanelWrapper stretch-for-sharing', {
|
||||
'lnsWorkspacePanelWrapper--fullscreen': isFullscreen,
|
||||
})}
|
||||
css={{ height: '100%' }}
|
||||
color="transparent"
|
||||
>
|
||||
<WorkspaceTitle />
|
||||
{children}
|
||||
<EuiFlexGroup
|
||||
gutterSize="none"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
direction="column"
|
||||
css={css`
|
||||
height: 100%;
|
||||
`}
|
||||
>
|
||||
<EuiFlexItem
|
||||
data-test-subj="lnsWorkspacePanelWrapper__innerContent"
|
||||
grow={false}
|
||||
css={{
|
||||
flexGrow: 0,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
...visDimensionsCSS,
|
||||
}}
|
||||
>
|
||||
<WorkspaceTitle />
|
||||
{children}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPageTemplate.Section>
|
||||
</EuiPageTemplate>
|
||||
);
|
||||
|
|
|
@ -92,6 +92,7 @@ export function ExpressionWrapper({
|
|||
syncTooltips={syncTooltips}
|
||||
syncCursor={syncCursor}
|
||||
executionContext={executionContext}
|
||||
shouldUseSizeTransitionVeil={true}
|
||||
renderError={(errorMessage, error) => {
|
||||
const messages = getOriginalRequestErrorMessages(error || null);
|
||||
addUserMessages(messages);
|
||||
|
|
|
@ -42,6 +42,7 @@ import { CellValueContext } from '@kbn/embeddable-plugin/public';
|
|||
import { EventAnnotationGroupConfig } from '@kbn/event-annotation-common';
|
||||
import type { DraggingIdentifier, DragDropIdentifier, DropType } from '@kbn/dom-drag-drop';
|
||||
import type { AccessorConfig } from '@kbn/visualization-ui-components';
|
||||
import type { ChartSizeEvent } from '@kbn/chart-expressions-common';
|
||||
import type { DateRange, LayerType, SortingHint } from '../common/types';
|
||||
import type {
|
||||
LensSortActionData,
|
||||
|
@ -1391,6 +1392,7 @@ export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandle
|
|||
| BrushTriggerEvent
|
||||
| LensEditEvent<LensEditSupportedActions>
|
||||
| LensTableRowContextMenuEvent
|
||||
| ChartSizeEvent
|
||||
) => void;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import type {
|
|||
IInterpreterRenderHandlers,
|
||||
} from '@kbn/expressions-plugin/common';
|
||||
import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { ChartSizeEvent } from '@kbn/chart-expressions-common';
|
||||
import { trackUiCounterEvents } from '../../lens_ui_telemetry';
|
||||
import { DatatableComponent } from './components/table_basic';
|
||||
|
||||
|
@ -103,6 +104,18 @@ export const getDatatableRenderer = (dependencies: {
|
|||
handlers.done();
|
||||
};
|
||||
|
||||
const chartSizeEvent: ChartSizeEvent = {
|
||||
name: 'chartSize',
|
||||
data: {
|
||||
maxDimensions: {
|
||||
x: { value: 100, unit: 'percentage' },
|
||||
y: { value: 100, unit: 'percentage' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
handlers.event(chartSizeEvent);
|
||||
|
||||
// An entry for each table row, whether it has any actions attached to
|
||||
// ROW_CLICK_TRIGGER trigger.
|
||||
let rowHasRowClickTriggerActions: boolean[] = [];
|
||||
|
|
|
@ -143,7 +143,7 @@ describe('metric visualization', () => {
|
|||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"color": "#f5f7fa",
|
||||
"color": "#ffffff",
|
||||
"columnId": "metric-col-id",
|
||||
"triggerIconType": "color",
|
||||
},
|
||||
|
@ -727,7 +727,7 @@ describe('metric visualization', () => {
|
|||
datasourceLayers
|
||||
) as ExpressionAstExpression
|
||||
).chain[1].arguments.color[0]
|
||||
).toBe(euiLightVars.euiColorLightestShade);
|
||||
).toBe(euiLightVars.euiColorEmptyShade);
|
||||
|
||||
expect(
|
||||
(
|
||||
|
@ -741,7 +741,7 @@ describe('metric visualization', () => {
|
|||
datasourceLayers
|
||||
) as ExpressionAstExpression
|
||||
).chain[1].arguments.color[0]
|
||||
).toBe(euiLightVars.euiColorLightestShade);
|
||||
).toBe(euiLightVars.euiColorEmptyShade);
|
||||
|
||||
// this case isn't currently relevant because other parts of the code don't allow showBar to be
|
||||
// set when there isn't a max dimension but this test covers the branch anyhow
|
||||
|
@ -757,7 +757,7 @@ describe('metric visualization', () => {
|
|||
datasourceLayers
|
||||
) as ExpressionAstExpression
|
||||
).chain[1].arguments.color[0]
|
||||
).toEqual(euiThemeVars.euiColorLightestShade);
|
||||
).toEqual(euiThemeVars.euiColorEmptyShade);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ export const showingBar = (
|
|||
export const getDefaultColor = (state: MetricVisualizationState, isMetricNumeric?: boolean) => {
|
||||
return showingBar(state) && isMetricNumeric
|
||||
? euiLightVars.euiColorPrimary
|
||||
: euiThemeVars.euiColorLightestShade;
|
||||
: euiThemeVars.euiColorEmptyShade;
|
||||
};
|
||||
|
||||
export interface MetricVisualizationState {
|
||||
|
|
|
@ -13,6 +13,7 @@ import { METRIC_TYPE } from '@kbn/analytics';
|
|||
import type { CoreSetup, CoreStart } from '@kbn/core/public';
|
||||
import type { FileLayer } from '@elastic/ems-client';
|
||||
import type { KibanaExecutionContext } from '@kbn/core-execution-context-common';
|
||||
import { ChartSizeEvent } from '@kbn/chart-expressions-common';
|
||||
import type { MapsPluginStartDependencies } from '../../plugin';
|
||||
import type { ChoroplethChartProps } from './types';
|
||||
import type { MapEmbeddableInput, MapEmbeddableOutput } from '../../embeddable';
|
||||
|
@ -92,6 +93,18 @@ export function getExpressionRenderer(coreSetup: CoreSetup<MapsPluginStartDepend
|
|||
handlers.done();
|
||||
};
|
||||
|
||||
const chartSizeEvent: ChartSizeEvent = {
|
||||
name: 'chartSize',
|
||||
data: {
|
||||
maxDimensions: {
|
||||
x: { value: 100, unit: 'percentage' },
|
||||
y: { value: 100, unit: 'percentage' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
handlers.event(chartSizeEvent);
|
||||
|
||||
ReactDOM.render(
|
||||
<ChoroplethChart
|
||||
{...config}
|
||||
|
|
|
@ -74,6 +74,7 @@
|
|||
"@kbn/content-management-table-list-view",
|
||||
"@kbn/serverless",
|
||||
"@kbn/logging",
|
||||
"@kbn/chart-expressions-common",
|
||||
"@kbn/search-errors",
|
||||
"@kbn/search-response-warnings",
|
||||
"@kbn/calculate-width-from-char-count",
|
||||
|
|
|
@ -72,7 +72,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: 'Maximum of bytes 19,986',
|
||||
value: '5,727.322',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingTrendline: false,
|
||||
showingBar: false,
|
||||
|
@ -138,7 +138,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '5,727.322',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingTrendline: false,
|
||||
showingBar: false,
|
||||
|
|
|
@ -80,8 +80,9 @@ export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext
|
|||
loadTestFile(require.resolve('./inspector')); // 1m 19s
|
||||
loadTestFile(require.resolve('./error_handling')); // 1m 8s
|
||||
loadTestFile(require.resolve('./lens_tagging')); // 1m 9s
|
||||
loadTestFile(require.resolve('./workspace_size'));
|
||||
// keep these last in the group in this order because they are messing with the default saved objects
|
||||
loadTestFile(require.resolve('./lens_reporting')); // 3m
|
||||
// keep these two last in the group in this order because they are messing with the default saved objects
|
||||
loadTestFile(require.resolve('./rollup')); // 1m 30s
|
||||
loadTestFile(require.resolve('./no_data')); // 36s
|
||||
});
|
||||
|
|
|
@ -127,8 +127,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
subtitle: 'Average of bytes',
|
||||
extraText: 'Average of bytes 19,755',
|
||||
value: '19,755',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
trendlineColor: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: true,
|
||||
showingBar: false,
|
||||
},
|
||||
|
@ -137,8 +137,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
subtitle: 'Average of bytes',
|
||||
extraText: 'Average of bytes 18,994',
|
||||
value: '18,994',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
trendlineColor: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: true,
|
||||
showingBar: false,
|
||||
},
|
||||
|
@ -147,8 +147,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
subtitle: 'Average of bytes',
|
||||
extraText: 'Average of bytes 17,246',
|
||||
value: '17,246',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
trendlineColor: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: true,
|
||||
showingBar: false,
|
||||
},
|
||||
|
@ -157,8 +157,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
subtitle: 'Average of bytes',
|
||||
extraText: 'Average of bytes 15,687',
|
||||
value: '15,687',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
trendlineColor: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: true,
|
||||
showingBar: false,
|
||||
},
|
||||
|
@ -167,8 +167,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
subtitle: 'Average of bytes',
|
||||
extraText: 'Average of bytes 15,614.333',
|
||||
value: '15,614.333',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
trendlineColor: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: true,
|
||||
showingBar: false,
|
||||
},
|
||||
|
@ -177,8 +177,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
subtitle: 'Average of bytes',
|
||||
extraText: 'Average of bytes 5,722.775',
|
||||
value: '5,722.775',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
trendlineColor: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: true,
|
||||
showingBar: false,
|
||||
},
|
||||
|
|
254
x-pack/test/functional/apps/lens/group6/workspace_size.ts
Normal file
254
x-pack/test/functional/apps/lens/group6/workspace_size.ts
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const PageObjects = getPageObjects(['visualize', 'lens', 'common']);
|
||||
const browser = getService('browser');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const log = getService('log');
|
||||
|
||||
describe('lens workspace size', () => {
|
||||
let originalWindowSize: {
|
||||
height: number;
|
||||
width: number;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
const DEFAULT_WINDOW_SIZE = [1400, 900];
|
||||
|
||||
before(async () => {
|
||||
originalWindowSize = await browser.getWindowSize();
|
||||
|
||||
await PageObjects.visualize.navigateToNewVisualization();
|
||||
await PageObjects.visualize.clickVisType('lens');
|
||||
await PageObjects.lens.goToTimeRange();
|
||||
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'average',
|
||||
field: 'bytes',
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.setWindowSize(DEFAULT_WINDOW_SIZE[0], DEFAULT_WINDOW_SIZE[1]);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await browser.setWindowSize(originalWindowSize.width, originalWindowSize.height);
|
||||
});
|
||||
|
||||
const pxToN = (pixels: string) => Number(pixels.substring(0, pixels.length - 2));
|
||||
|
||||
const assertWorkspaceDimensions = async (expectedWidth: string, expectedHeight: string) => {
|
||||
const tolerance = 1;
|
||||
|
||||
await retry.try(async () => {
|
||||
const { width, height } = await PageObjects.lens.getWorkspaceVisContainerDimensions();
|
||||
|
||||
expect(pxToN(width)).to.within(
|
||||
pxToN(expectedWidth) - tolerance,
|
||||
pxToN(expectedWidth) + tolerance
|
||||
);
|
||||
expect(pxToN(height)).to.within(
|
||||
pxToN(expectedHeight) - tolerance,
|
||||
pxToN(expectedHeight) + tolerance
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const assertWorkspaceAspectRatio = async (expectedRatio: number) => {
|
||||
const tolerance = 0.05;
|
||||
|
||||
await retry.try(async () => {
|
||||
const { width, height } = await PageObjects.lens.getWorkspaceVisContainerDimensions();
|
||||
|
||||
expect(pxToN(width) / pxToN(height)).to.within(
|
||||
expectedRatio - tolerance,
|
||||
expectedRatio + tolerance
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const assertWorkspaceStyles = async (expectedStyles: {
|
||||
aspectRatio: string;
|
||||
minHeight: string;
|
||||
minWidth: string;
|
||||
maxHeight: string;
|
||||
maxWidth: string;
|
||||
}) => {
|
||||
const actualStyles = await PageObjects.lens.getWorkspaceVisContainerStyles();
|
||||
|
||||
expect(actualStyles).to.eql(expectedStyles);
|
||||
};
|
||||
|
||||
const VERTICAL_16_9 = 16 / 9;
|
||||
const outerWorkspaceDimensions = { width: 690, height: 400 };
|
||||
const UNCONSTRAINED = outerWorkspaceDimensions.width / outerWorkspaceDimensions.height;
|
||||
|
||||
it('workspace size recovers from special vis types', async () => {
|
||||
/**
|
||||
* This list is specifically designed to test dimension transitions.
|
||||
*
|
||||
* I have attempted to order the vis types to maximize the number of transitions.
|
||||
*
|
||||
* Excluding XY charts since they are tested separately.
|
||||
*/
|
||||
const visTypes: Array<{
|
||||
id: string;
|
||||
searchText?: string;
|
||||
expectedHeight?: string;
|
||||
expectedWidth?: string;
|
||||
aspectRatio?: number;
|
||||
}> = [
|
||||
{
|
||||
id: 'lnsMetric',
|
||||
expectedWidth: '300px',
|
||||
expectedHeight: '300px',
|
||||
},
|
||||
{ id: 'lnsDatatable', aspectRatio: UNCONSTRAINED },
|
||||
{
|
||||
id: 'lnsMetric',
|
||||
expectedWidth: '300px',
|
||||
expectedHeight: '300px',
|
||||
},
|
||||
{ id: 'lnsLegacyMetric', aspectRatio: UNCONSTRAINED },
|
||||
{
|
||||
id: 'lnsMetric',
|
||||
expectedWidth: '300px',
|
||||
expectedHeight: '300px',
|
||||
},
|
||||
{ id: 'donut', aspectRatio: UNCONSTRAINED },
|
||||
{
|
||||
id: 'lnsMetric',
|
||||
expectedWidth: '300px',
|
||||
expectedHeight: '300px',
|
||||
},
|
||||
{ id: 'mosaic', aspectRatio: UNCONSTRAINED },
|
||||
{
|
||||
id: 'lnsMetric',
|
||||
expectedWidth: '300px',
|
||||
expectedHeight: '300px',
|
||||
},
|
||||
{ id: 'pie', aspectRatio: UNCONSTRAINED },
|
||||
{
|
||||
id: 'lnsMetric',
|
||||
expectedWidth: '300px',
|
||||
expectedHeight: '300px',
|
||||
},
|
||||
{ id: 'treemap', aspectRatio: UNCONSTRAINED },
|
||||
{
|
||||
id: 'lnsMetric',
|
||||
expectedWidth: '300px',
|
||||
expectedHeight: '300px',
|
||||
},
|
||||
{ id: 'waffle', aspectRatio: UNCONSTRAINED },
|
||||
// { id: 'heatmap', ...UNCONSTRAINED }, // heatmap blocks render unless it's given two dimensions. This stops the expression renderer from requesting new dimensions.
|
||||
// { id: 'lnsChoropleth', ...UNCONSTRAINED }, // choropleth currently erases all dimensions
|
||||
// { id: 'lnsTagcloud', ...UNCONSTRAINED }, // tag cloud currently erases all dimensions
|
||||
];
|
||||
|
||||
while (visTypes.length) {
|
||||
const vis = visTypes.pop()!;
|
||||
await retry.try(async () => {
|
||||
await PageObjects.lens.switchToVisualization(vis.id, vis.searchText);
|
||||
});
|
||||
|
||||
log.debug(`Testing ${vis.id}... expecting ${vis.expectedWidth}x${vis.expectedHeight}`);
|
||||
|
||||
if (vis.aspectRatio) {
|
||||
await assertWorkspaceAspectRatio(vis.aspectRatio);
|
||||
} else {
|
||||
await assertWorkspaceDimensions(vis.expectedWidth!, vis.expectedHeight!);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('metric size (absolute pixels)', async () => {
|
||||
await retry.try(async () => {
|
||||
await PageObjects.lens.switchToVisualization('lnsMetric');
|
||||
});
|
||||
|
||||
await assertWorkspaceDimensions('300px', '300px');
|
||||
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsMetric_breakdownByDimensionPanel > lns-empty-dimension',
|
||||
operation: 'terms',
|
||||
field: 'ip',
|
||||
});
|
||||
|
||||
await assertWorkspaceDimensions('600px', '400px');
|
||||
|
||||
await PageObjects.lens.openDimensionEditor('lnsMetric_breakdownByDimensionPanel');
|
||||
await testSubjects.setValue('lnsMetric_max_cols', '2');
|
||||
|
||||
await assertWorkspaceDimensions('400px', '400px');
|
||||
});
|
||||
|
||||
it('gauge size (absolute pixels)', async () => {
|
||||
await retry.try(async () => {
|
||||
await PageObjects.lens.switchToVisualization('horizontalBullet', 'gauge');
|
||||
});
|
||||
|
||||
await assertWorkspaceDimensions('600px', '300px');
|
||||
|
||||
await retry.try(async () => {
|
||||
await PageObjects.lens.switchToVisualization('verticalBullet', 'gauge');
|
||||
});
|
||||
|
||||
// this height is below the requested 600px
|
||||
// that is because the window size isn't large enough to fit the requested dimensions
|
||||
// and the chart is forced to shrink.
|
||||
//
|
||||
// this is a good thing because it makes this a test case for that scenario
|
||||
await assertWorkspaceDimensions('300px', '400px');
|
||||
});
|
||||
|
||||
it('XY chart size', async () => {
|
||||
// XY charts should have 100% width and 100% height unless they are a vertical chart with a time dimension
|
||||
await retry.try(async () => {
|
||||
// not important that this is specifically a line chart
|
||||
await PageObjects.lens.switchToVisualization('line');
|
||||
});
|
||||
|
||||
await assertWorkspaceStyles({
|
||||
aspectRatio: 'auto',
|
||||
minHeight: 'auto',
|
||||
minWidth: 'auto',
|
||||
maxHeight: '100%',
|
||||
maxWidth: '100%',
|
||||
});
|
||||
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'date_histogram',
|
||||
field: '@timestamp',
|
||||
});
|
||||
|
||||
await assertWorkspaceStyles({
|
||||
aspectRatio: '16 / 9',
|
||||
minHeight: '300px',
|
||||
minWidth: '100%',
|
||||
maxHeight: 'none',
|
||||
maxWidth: 'none',
|
||||
});
|
||||
|
||||
await assertWorkspaceAspectRatio(VERTICAL_16_9);
|
||||
|
||||
await retry.try(async () => {
|
||||
await PageObjects.lens.switchToVisualization('bar_horizontal_stacked');
|
||||
});
|
||||
|
||||
await assertWorkspaceAspectRatio(UNCONSTRAINED);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -48,7 +48,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '140.05%',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
|
@ -80,7 +80,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '131,040,360.81%',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
|
@ -112,7 +112,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '14.37%',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
|
@ -156,7 +156,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '65,047,486.03',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
|
@ -166,7 +166,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '66,144,823.35',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
|
@ -176,7 +176,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '65,933,477.76',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
|
@ -186,7 +186,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '65,157,898.23',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
|
@ -196,7 +196,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '65,365,950.93',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
|
|
|
@ -49,7 +49,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '14,005',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
|
@ -80,7 +80,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '13,104,036,080.615',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
|
@ -111,7 +111,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '1,437',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
|
@ -166,7 +166,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,228,964,670.613',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
|
@ -176,7 +176,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,186,695,551.251',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
|
@ -186,7 +186,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,073,190,186.423',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
|
@ -196,7 +196,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,031,579,645.108',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
|
@ -206,7 +206,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,009,497,206.823',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
trendlineColor: undefined,
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
|
|
|
@ -1947,5 +1947,28 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
|
||||
await this.closeDimensionEditor();
|
||||
},
|
||||
|
||||
async getWorkspaceVisContainerDimensions() {
|
||||
const visContainer = await testSubjects.find('lnsWorkspacePanelWrapper__innerContent');
|
||||
const [width, height] = await Promise.all([
|
||||
visContainer.getComputedStyle('width'),
|
||||
visContainer.getComputedStyle('height'),
|
||||
]);
|
||||
|
||||
return { width, height };
|
||||
},
|
||||
|
||||
async getWorkspaceVisContainerStyles() {
|
||||
const visContainer = await testSubjects.find('lnsWorkspacePanelWrapper__innerContent');
|
||||
const [maxWidth, maxHeight, minWidth, minHeight, aspectRatio] = await Promise.all([
|
||||
visContainer.getComputedStyle('max-width'),
|
||||
visContainer.getComputedStyle('max-height'),
|
||||
visContainer.getComputedStyle('min-width'),
|
||||
visContainer.getComputedStyle('min-height'),
|
||||
visContainer.getComputedStyle('aspect-ratio'),
|
||||
]);
|
||||
|
||||
return { maxWidth, maxHeight, minWidth, minHeight, aspectRatio };
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '140.05%',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
},
|
||||
|
@ -77,7 +77,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '131,040,360.81%',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
},
|
||||
|
@ -105,7 +105,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '14.37%',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingBar: true,
|
||||
showingTrendline: false,
|
||||
},
|
||||
|
@ -133,7 +133,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,228,964,670.613',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: false,
|
||||
showingBar: true,
|
||||
},
|
||||
|
@ -142,7 +142,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,186,695,551.251',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: false,
|
||||
showingBar: true,
|
||||
},
|
||||
|
@ -151,7 +151,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,073,190,186.423',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: false,
|
||||
showingBar: true,
|
||||
},
|
||||
|
@ -160,7 +160,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,031,579,645.108',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: false,
|
||||
showingBar: true,
|
||||
},
|
||||
|
@ -169,7 +169,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: 'Average machine.ram',
|
||||
extraText: '',
|
||||
value: '13,009,497,206.823',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingTrendline: false,
|
||||
showingBar: true,
|
||||
},
|
||||
|
|
|
@ -46,7 +46,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '14,005',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
},
|
||||
|
@ -72,7 +72,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '13,104,036,080.615',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
},
|
||||
|
@ -99,7 +99,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
|
|||
subtitle: undefined,
|
||||
extraText: '',
|
||||
value: '1,437',
|
||||
color: 'rgba(245, 247, 250, 1)',
|
||||
color: 'rgba(255, 255, 255, 1)',
|
||||
showingBar: false,
|
||||
showingTrendline: false,
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue