mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Infra UI] Chart load optimization (#162328)
Closes https://github.com/elastic/kibana/issues/161445 ## Summary This PR enhances the usage of the intersection observer to control the loading behavior of charts, improving the performance, especially noticeable during scrolling. Beforedfb1d7db
-4ddb-41c3-b8ae-3b5bdf3fe36e Afterf348b0b9
-4e6f-4163-9eb4-99daea91bbef Besides, the intersection observer threshold has been set to 0, allowing charts to start loading as soon as they begin to enter the viewport. ### How to test - Start a local Kibana instance - Navigate to `Infrastructure > Hosts` - Scroll down and confirm that it's not laggy (same for the flyout) --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
0cc81d73f7
commit
036751463d
20 changed files with 231 additions and 268 deletions
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiProgress, EuiFlexItem, EuiLoadingChart, useEuiTheme } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
|
||||
export const ChartLoader = ({
|
||||
children,
|
||||
loading,
|
||||
style,
|
||||
loadedOnce = false,
|
||||
hasTitle = false,
|
||||
}: {
|
||||
style?: React.CSSProperties;
|
||||
children: React.ReactNode;
|
||||
loadedOnce: boolean;
|
||||
loading: boolean;
|
||||
hasTitle?: boolean;
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
return (
|
||||
<LoaderContainer>
|
||||
{loading && (
|
||||
<EuiProgress
|
||||
size="xs"
|
||||
color="accent"
|
||||
position="absolute"
|
||||
css={css`
|
||||
top: ${loadedOnce && hasTitle ? euiTheme.size.l : 0};
|
||||
z-index: ${Number(euiTheme.levels.header) - 1};
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
{loading && !loadedOnce ? (
|
||||
<EuiFlexGroup
|
||||
style={{ ...style, marginTop: hasTitle ? euiTheme.size.l : 0 }}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
responsive={false}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingChart mono size="l" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</LoaderContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const LoaderContainer = euiStyled.div`
|
||||
position: relative;
|
||||
border-radius: ${({ theme }) => theme.eui.euiSizeS};
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
`;
|
|
@ -1,144 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
||||
|
||||
import { Action } from '@kbn/ui-actions-plugin/public';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { TypedLensByValueInput } from '@kbn/lens-plugin/public';
|
||||
import { css } from '@emotion/react';
|
||||
import { useKibanaContextForPlugin } from '../../../hooks/use_kibana';
|
||||
import { useIntersectedOnce } from '../../../hooks/use_intersection_once';
|
||||
import { ChartLoader } from './chart_loader';
|
||||
import type { LensAttributes } from '../types';
|
||||
|
||||
export type LensWrapperProps = Pick<
|
||||
TypedLensByValueInput,
|
||||
| 'id'
|
||||
| 'filters'
|
||||
| 'query'
|
||||
| 'style'
|
||||
| 'onBrushEnd'
|
||||
| 'hidePanelTitles'
|
||||
| 'overrides'
|
||||
| 'hidePanelTitles'
|
||||
| 'disabledActions'
|
||||
| 'disableTriggers'
|
||||
> & {
|
||||
attributes: LensAttributes | null;
|
||||
dateRange: TimeRange;
|
||||
extraActions: Action[];
|
||||
lastReloadRequestTime?: number;
|
||||
loading?: boolean;
|
||||
hasTitle?: boolean;
|
||||
};
|
||||
|
||||
export const LensWrapper = React.memo(
|
||||
({
|
||||
attributes,
|
||||
dateRange,
|
||||
filters,
|
||||
id,
|
||||
query,
|
||||
extraActions,
|
||||
style,
|
||||
onBrushEnd,
|
||||
lastReloadRequestTime,
|
||||
overrides,
|
||||
loading = false,
|
||||
hasTitle = false,
|
||||
disableTriggers = false,
|
||||
}: LensWrapperProps) => {
|
||||
const intersectionRef = useRef(null);
|
||||
const [loadedOnce, setLoadedOnce] = useState(false);
|
||||
|
||||
const [state, setState] = useState({
|
||||
attributes,
|
||||
lastReloadRequestTime,
|
||||
query,
|
||||
filters,
|
||||
dateRange,
|
||||
});
|
||||
|
||||
const {
|
||||
services: { lens },
|
||||
} = useKibanaContextForPlugin();
|
||||
const { intersectedOnce, intersection } = useIntersectedOnce(intersectionRef, {
|
||||
threshold: 1,
|
||||
});
|
||||
|
||||
const EmbeddableComponent = lens.EmbeddableComponent;
|
||||
|
||||
useEffect(() => {
|
||||
if ((intersection?.intersectionRatio ?? 0) === 1) {
|
||||
setState({
|
||||
attributes,
|
||||
lastReloadRequestTime,
|
||||
query,
|
||||
filters,
|
||||
dateRange,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
attributes,
|
||||
dateRange,
|
||||
filters,
|
||||
intersection?.intersectionRatio,
|
||||
lastReloadRequestTime,
|
||||
query,
|
||||
]);
|
||||
|
||||
const isReady = state.attributes && intersectedOnce;
|
||||
|
||||
const onLoad = useCallback(() => {
|
||||
if (!loadedOnce) {
|
||||
setLoadedOnce(true);
|
||||
}
|
||||
}, [loadedOnce]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={intersectionRef}
|
||||
css={css`
|
||||
.echLegend .echLegendList {
|
||||
display: flex;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<ChartLoader
|
||||
loading={loading || !isReady}
|
||||
loadedOnce={loadedOnce}
|
||||
style={style}
|
||||
hasTitle={hasTitle}
|
||||
>
|
||||
{state.attributes && (
|
||||
<EmbeddableComponent
|
||||
id={id}
|
||||
style={style}
|
||||
hidePanelTitles={!hasTitle}
|
||||
attributes={state.attributes}
|
||||
viewMode={ViewMode.VIEW}
|
||||
timeRange={state.dateRange}
|
||||
query={state.query}
|
||||
filters={state.filters}
|
||||
extraActions={extraActions}
|
||||
overrides={overrides}
|
||||
lastReloadRequestTime={state.lastReloadRequestTime}
|
||||
executionContext={{
|
||||
type: 'infrastructure_observability_hosts_view',
|
||||
name: id,
|
||||
}}
|
||||
onBrushEnd={onBrushEnd}
|
||||
onLoad={onLoad}
|
||||
disableTriggers={disableTriggers}
|
||||
/>
|
||||
)}
|
||||
</ChartLoader>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -45,7 +45,7 @@ export class XYDataLayer implements ChartLayer<XYDataLayerConfig> {
|
|||
return this.column[0].getFormulaConfig().label;
|
||||
}
|
||||
|
||||
getBaseColumnColumn(dataView: DataView, options?: XYLayerOptions) {
|
||||
getBaseLayer(dataView: DataView, options?: XYLayerOptions) {
|
||||
return {
|
||||
...getHistogramColumn({
|
||||
columnName: HISTOGRAM_COLUMN_NAME,
|
||||
|
@ -75,7 +75,7 @@ export class XYDataLayer implements ChartLayer<XYDataLayerConfig> {
|
|||
const baseLayer: PersistedIndexPatternLayer = {
|
||||
columnOrder: [BREAKDOWN_COLUMN_NAME, HISTOGRAM_COLUMN_NAME],
|
||||
columns: {
|
||||
...this.getBaseColumnColumn(dataView, this.layerConfig.options),
|
||||
...this.getBaseLayer(dataView, this.layerConfig.options),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -12,12 +12,7 @@ import { KPI_CHARTS } from '../../../../../common/visualizations/lens/dashboards
|
|||
export const KPIGrid = React.memo(({ nodeName, dataView, timeRange: dateRange }: TileProps) => {
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
style={{ flexGrow: 0 }}
|
||||
data-test-subj="assetDetailsKPIGrid"
|
||||
>
|
||||
<EuiFlexGroup direction="row" gutterSize="s" data-test-subj="assetDetailsKPIGrid">
|
||||
{KPI_CHARTS.map((chartProp, index) => (
|
||||
<EuiFlexItem key={index}>
|
||||
<Tile {...chartProp} nodeName={nodeName} dataView={dataView} timeRange={dateRange} />
|
||||
|
|
|
@ -12,11 +12,10 @@ import styled from 'styled-components';
|
|||
import type { Action } from '@kbn/ui-actions-plugin/public';
|
||||
import { TimeRange } from '@kbn/es-query';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { LensWrapper, TooltipContent } from '../../../../lens';
|
||||
import type { KPIChartProps } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config';
|
||||
import { useLensAttributes } from '../../../../../hooks/use_lens_attributes';
|
||||
import { LensWrapper } from '../../../../../common/visualizations/lens/lens_wrapper';
|
||||
import { buildCombinedHostsFilter } from '../../../../../utils/filters/build';
|
||||
import { TooltipContent } from '../../../../../common/visualizations/metric_explanation/tooltip_content';
|
||||
|
||||
const MIN_HEIGHT = 150;
|
||||
|
||||
|
@ -72,7 +71,6 @@ export const Tile = ({
|
|||
<EuiPanelStyled
|
||||
hasShadow={false}
|
||||
paddingSize={error ? 'm' : 'none'}
|
||||
style={{ minHeight: MIN_HEIGHT }}
|
||||
data-test-subj={`assetDetailsKPI-${id}`}
|
||||
>
|
||||
{error ? (
|
||||
|
@ -109,6 +107,7 @@ export const Tile = ({
|
|||
dateRange={timeRange}
|
||||
filters={filters}
|
||||
loading={loading}
|
||||
hidePanelTitles
|
||||
/>
|
||||
</EuiToolTip>
|
||||
)}
|
||||
|
@ -117,6 +116,7 @@ export const Tile = ({
|
|||
};
|
||||
|
||||
const EuiPanelStyled = styled(EuiPanel)`
|
||||
min-height: ${MIN_HEIGHT}px;
|
||||
.echMetric {
|
||||
border-radius: ${({ theme }) => theme.eui.euiBorderRadius};
|
||||
pointer-events: none;
|
||||
|
|
|
@ -12,8 +12,8 @@ import { TypedLensByValueInput } from '@kbn/lens-plugin/public';
|
|||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { LensWrapper } from '../../../../lens/lens_wrapper';
|
||||
import { buildCombinedHostsFilter } from '../../../../../utils/filters/build';
|
||||
import { LensWrapper } from '../../../../../common/visualizations/lens/lens_wrapper';
|
||||
import { useLensAttributes, type Layer } from '../../../../../hooks/use_lens_attributes';
|
||||
import type { FormulaConfig, XYLayerOptions } from '../../../../../common/visualizations';
|
||||
|
||||
|
@ -109,7 +109,6 @@ export const MetricChart = ({
|
|||
overrides={overrides}
|
||||
loading={loading}
|
||||
disableTriggers
|
||||
hasTitle
|
||||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
|
|
|
@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { TimeRange } from '@kbn/es-query';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { HostMetricsDocsLink } from '../../../../../common/visualizations/metric_explanation/host_metrics_docs_link';
|
||||
import { HostMetricsDocsLink } from '../../../../lens';
|
||||
import { MetricChart, type MetricChartProps } from './metric_chart';
|
||||
import { hostLensFormulas } from '../../../../../common/visualizations';
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { EuiFlexGroup, EuiProgress, EuiFlexItem, EuiLoadingChart } from '@elastic/eui';
|
||||
import { useEuiTheme } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
|
||||
export const ChartLoadingProgress = ({ hasTopMargin = false }: { hasTopMargin?: boolean }) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
return (
|
||||
<EuiProgress
|
||||
size="xs"
|
||||
color="accent"
|
||||
position="absolute"
|
||||
css={css`
|
||||
top: ${hasTopMargin ? euiTheme.size.l : 0};
|
||||
z-index: ${Number(euiTheme.levels.header) - 1};
|
||||
`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ChartPlaceholder = ({ style }: { style?: React.CSSProperties }) => {
|
||||
return (
|
||||
<>
|
||||
<ChartLoadingProgress hasTopMargin={false} />
|
||||
<EuiFlexGroup style={style} justifyContent="center" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingChart mono size="l" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
);
|
||||
};
|
12
x-pack/plugins/infra/public/components/lens/index.tsx
Normal file
12
x-pack/plugins/infra/public/components/lens/index.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { ChartPlaceholder } from './chart_placeholder';
|
||||
export { LensWrapper } from './lens_wrapper';
|
||||
|
||||
export { TooltipContent } from './metric_explanation/tooltip_content';
|
||||
export { HostMetricsDocsLink } from './metric_explanation/host_metrics_docs_link';
|
154
x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx
Normal file
154
x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
||||
import type { Action } from '@kbn/ui-actions-plugin/public';
|
||||
import { ViewMode } from '@kbn/embeddable-plugin/public';
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { TypedLensByValueInput } from '@kbn/lens-plugin/public';
|
||||
import { euiStyled } from '@kbn/kibana-react-plugin/common';
|
||||
import { useKibanaContextForPlugin } from '../../hooks/use_kibana';
|
||||
import { ChartLoadingProgress, ChartPlaceholder } from './chart_placeholder';
|
||||
import { parseDateRange } from '../../utils/datemath';
|
||||
import { LensAttributes } from '../../common/visualizations';
|
||||
|
||||
export type LensWrapperProps = Omit<
|
||||
TypedLensByValueInput,
|
||||
'timeRange' | 'attributes' | 'viewMode'
|
||||
> & {
|
||||
attributes: LensAttributes | null;
|
||||
dateRange: TimeRange;
|
||||
extraActions: Action[];
|
||||
loading?: boolean;
|
||||
};
|
||||
|
||||
export const LensWrapper = ({
|
||||
attributes,
|
||||
dateRange,
|
||||
filters,
|
||||
lastReloadRequestTime,
|
||||
loading,
|
||||
query,
|
||||
...props
|
||||
}: LensWrapperProps) => {
|
||||
const [intersectionObserverEntry, setIntersectionObserverEntry] =
|
||||
useState<IntersectionObserverEntry>();
|
||||
const [embeddableLoaded, setEmbeddableLoaded] = useState(false);
|
||||
const [state, setState] = useState({
|
||||
attributes,
|
||||
dateRange,
|
||||
filters,
|
||||
lastReloadRequestTime,
|
||||
query,
|
||||
});
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const observerRef = useRef(
|
||||
new IntersectionObserver(([value]) => setIntersectionObserverEntry(value), {
|
||||
root: ref.current,
|
||||
})
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const { current: currentObserver } = observerRef;
|
||||
currentObserver.disconnect();
|
||||
const { current } = ref;
|
||||
|
||||
if (current) {
|
||||
currentObserver.observe(current);
|
||||
}
|
||||
|
||||
return () => currentObserver.disconnect();
|
||||
}, [ref]);
|
||||
|
||||
useEffect(() => {
|
||||
if (intersectionObserverEntry?.isIntersecting) {
|
||||
setState({
|
||||
attributes,
|
||||
dateRange,
|
||||
filters,
|
||||
lastReloadRequestTime,
|
||||
query,
|
||||
});
|
||||
}
|
||||
}, [
|
||||
attributes,
|
||||
dateRange,
|
||||
filters,
|
||||
intersectionObserverEntry?.isIntersecting,
|
||||
lastReloadRequestTime,
|
||||
query,
|
||||
]);
|
||||
|
||||
const onLoad = useCallback(() => {
|
||||
if (!embeddableLoaded) {
|
||||
setEmbeddableLoaded(true);
|
||||
}
|
||||
}, [embeddableLoaded]);
|
||||
|
||||
const parsedDateRange: TimeRange = useMemo(() => {
|
||||
const { from = state.dateRange.from, to = state.dateRange.to } = parseDateRange(
|
||||
state.dateRange
|
||||
);
|
||||
|
||||
return { from, to };
|
||||
}, [state.dateRange]);
|
||||
|
||||
const isLoading = loading || !state.attributes;
|
||||
|
||||
return (
|
||||
<Container ref={ref}>
|
||||
<>
|
||||
{isLoading && !embeddableLoaded ? (
|
||||
<ChartPlaceholder style={props.style} />
|
||||
) : (
|
||||
<>
|
||||
{isLoading && <ChartLoadingProgress hasTopMargin={!props.hidePanelTitles} />}
|
||||
<EmbeddableComponentMemo
|
||||
{...props}
|
||||
attributes={state.attributes}
|
||||
filters={state.filters}
|
||||
lastReloadRequestTime={state.lastReloadRequestTime}
|
||||
onLoad={onLoad}
|
||||
query={state.query}
|
||||
timeRange={parsedDateRange}
|
||||
viewMode={ViewMode.VIEW}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
const EmbeddableComponentMemo = React.memo(
|
||||
({
|
||||
attributes,
|
||||
...props
|
||||
}: Omit<TypedLensByValueInput, 'attributes'> & { attributes: LensAttributes | null }) => {
|
||||
const {
|
||||
services: { lens },
|
||||
} = useKibanaContextForPlugin();
|
||||
|
||||
const EmbeddableComponent = lens.EmbeddableComponent;
|
||||
|
||||
if (!attributes) {
|
||||
return <ChartPlaceholder style={props.style} />;
|
||||
}
|
||||
|
||||
return <EmbeddableComponent {...props} attributes={attributes} />;
|
||||
}
|
||||
);
|
||||
|
||||
const Container = euiStyled.div`
|
||||
position: relative;
|
||||
border-radius: ${({ theme }) => theme.eui.euiSizeS};
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
.echLegend .echLegendList {
|
||||
display: flex;
|
||||
}
|
||||
`;
|
|
@ -8,7 +8,7 @@
|
|||
import React from 'react';
|
||||
import { EuiLink, EuiText } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { HOST_METRICS_DOC_HREF } from '../constants';
|
||||
import { HOST_METRICS_DOC_HREF } from '../../../common/visualizations/constants';
|
||||
|
||||
export const HostMetricsDocsLink = () => {
|
||||
return (
|
|
@ -9,7 +9,7 @@ import React, { HTMLAttributes } from 'react';
|
|||
import { EuiText, EuiLink } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { HOST_METRICS_DOC_HREF } from '../constants';
|
||||
import { HOST_METRICS_DOC_HREF } from '../../../common/visualizations/constants';
|
||||
|
||||
interface Props extends Pick<HTMLAttributes<HTMLDivElement>, 'style'> {
|
||||
description: string;
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RefObject, useEffect, useState } from 'react';
|
||||
import useIntersection from 'react-use/lib/useIntersection';
|
||||
|
||||
export const useIntersectedOnce = (
|
||||
ref: RefObject<HTMLElement>,
|
||||
options: IntersectionObserverInit
|
||||
) => {
|
||||
const [intersectedOnce, setIntersectedOnce] = useState(false);
|
||||
const intersection = useIntersection(ref, options);
|
||||
|
||||
useEffect(() => {
|
||||
if (!intersectedOnce && (intersection?.intersectionRatio ?? 0) > 0) {
|
||||
setIntersectedOnce(true);
|
||||
}
|
||||
}, [intersectedOnce, intersection?.intersectionRatio]);
|
||||
|
||||
return { intersectedOnce, intersection };
|
||||
};
|
|
@ -8,7 +8,7 @@ import React, { useEffect, useRef, CSSProperties } from 'react';
|
|||
import { Chart, Metric, type MetricWNumber, type MetricWTrend } from '@elastic/charts';
|
||||
import { EuiPanel, EuiToolTip } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { ChartLoader } from '../../../../../common/visualizations/lens/chart_loader';
|
||||
import { ChartPlaceholder } from '../../../../../components/lens';
|
||||
|
||||
export interface Props extends Pick<MetricWTrend, 'title' | 'color' | 'extra' | 'subtitle'> {
|
||||
id: string;
|
||||
|
@ -43,7 +43,9 @@ export const MetricChartWrapper = React.memo(
|
|||
|
||||
return (
|
||||
<EuiPanel hasShadow={false} paddingSize="none" {...props}>
|
||||
<ChartLoader loading={loading} loadedOnce={loadedOnce.current} style={style}>
|
||||
{loading && !loadedOnce.current ? (
|
||||
<ChartPlaceholder style={style} />
|
||||
) : (
|
||||
<EuiToolTip
|
||||
className="eui-fullWidth"
|
||||
delay="regular"
|
||||
|
@ -54,7 +56,7 @@ export const MetricChartWrapper = React.memo(
|
|||
<Metric id={id} data={[[metricsData]]} />
|
||||
</KPIChartStyled>
|
||||
</EuiToolTip>
|
||||
</ChartLoader>
|
||||
)}
|
||||
</EuiPanel>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
|
|||
import { TOOLTIP } from '../../../../../common/visualizations/lens/dashboards/host/translations';
|
||||
|
||||
import { type Props, MetricChartWrapper } from '../chart/metric_chart_wrapper';
|
||||
import { TooltipContent } from '../../../../../common/visualizations/metric_explanation/tooltip_content';
|
||||
import { TooltipContent } from '../../../../../components/lens';
|
||||
|
||||
const HOSTS_CHART: Omit<Props, 'loading' | 'value' | 'toolTip'> = {
|
||||
id: `metric-hostCount`,
|
||||
|
|
|
@ -8,7 +8,7 @@ import React, { CSSProperties } from 'react';
|
|||
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { HostMetricsDocsLink } from '../../../../../common/visualizations/metric_explanation/host_metrics_docs_link';
|
||||
import { HostMetricsDocsLink } from '../../../../../components/lens';
|
||||
import { Tile } from './tile';
|
||||
import { HostCountProvider } from '../../hooks/use_host_count';
|
||||
import { HostsTile } from './hosts_tile';
|
||||
|
@ -24,12 +24,7 @@ export const KPIGrid = () => {
|
|||
<HostCountProvider>
|
||||
<HostMetricsDocsLink />
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFlexGroup
|
||||
direction="row"
|
||||
gutterSize="s"
|
||||
style={{ flexGrow: 0 }}
|
||||
data-test-subj="hostsViewKPIGrid"
|
||||
>
|
||||
<EuiFlexGroup direction="row" gutterSize="s" data-test-subj="hostsViewKPIGrid">
|
||||
<EuiFlexItem>
|
||||
<HostsTile style={lensStyle} />
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -12,19 +12,18 @@ import { EuiIcon, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } fro
|
|||
import styled from 'styled-components';
|
||||
import { Action } from '@kbn/ui-actions-plugin/public';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { LensWrapper, TooltipContent } from '../../../../../components/lens';
|
||||
import { KPIChartProps } from '../../../../../common/visualizations/lens/dashboards/host/kpi_grid_config';
|
||||
import { buildCombinedHostsFilter } from '../../../../../utils/filters/build';
|
||||
import { useLensAttributes } from '../../../../../hooks/use_lens_attributes';
|
||||
import { useMetricsDataViewContext } from '../../hooks/use_data_view';
|
||||
import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
|
||||
import { useHostsViewContext } from '../../hooks/use_hosts_view';
|
||||
import { LensWrapper } from '../../../../../common/visualizations/lens/lens_wrapper';
|
||||
import { useHostCountContext } from '../../hooks/use_host_count';
|
||||
import { useAfterLoadedState } from '../../hooks/use_after_loaded_state';
|
||||
import { TooltipContent } from '../../../../../common/visualizations/metric_explanation/tooltip_content';
|
||||
import { KPI_CHART_MIN_HEIGHT } from '../../constants';
|
||||
|
||||
export const Tile = ({ id, title, layers, style, toolTip, ...props }: KPIChartProps) => {
|
||||
export const Tile = ({ id, title, layers, style, toolTip }: KPIChartProps) => {
|
||||
const { searchCriteria, onSubmit } = useUnifiedSearchContext();
|
||||
const { dataView } = useMetricsDataViewContext();
|
||||
const { requestTs, hostNodes, loading: hostsLoading } = useHostsViewContext();
|
||||
|
@ -149,6 +148,7 @@ export const Tile = ({ id, title, layers, style, toolTip, ...props }: KPIChartPr
|
|||
query={shouldUseSearchCriteria ? afterLoadedState.query : undefined}
|
||||
onBrushEnd={handleBrushEnd}
|
||||
loading={loading}
|
||||
hidePanelTitles
|
||||
/>
|
||||
</div>
|
||||
</EuiToolTip>
|
||||
|
@ -158,7 +158,7 @@ export const Tile = ({ id, title, layers, style, toolTip, ...props }: KPIChartPr
|
|||
};
|
||||
|
||||
const EuiPanelStyled = styled(EuiPanel)`
|
||||
min-height: ${KPI_CHART_MIN_HEIGHT};
|
||||
min-height: ${KPI_CHART_MIN_HEIGHT}px;
|
||||
.echMetric {
|
||||
border-radius: ${({ theme }) => theme.eui.euiBorderRadius};
|
||||
pointer-events: none;
|
||||
|
|
|
@ -8,7 +8,7 @@ import React, { useState, useRef, useCallback, useLayoutEffect } from 'react';
|
|||
import { EuiPopover, EuiIcon, EuiFlexGroup, useEuiTheme } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { APP_WRAPPER_CLASS } from '@kbn/core/public';
|
||||
import { TooltipContent } from '../../../../../common/visualizations/metric_explanation/tooltip_content';
|
||||
import { TooltipContent } from '../../../../../components/lens/metric_explanation/tooltip_content';
|
||||
import { useBoolean } from '../../../../../hooks/use_boolean';
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -11,7 +11,7 @@ import { EuiIcon, EuiPanel, EuiFlexGroup, EuiFlexItem, EuiText, useEuiTheme } fr
|
|||
import { css } from '@emotion/react';
|
||||
import { TypedLensByValueInput } from '@kbn/lens-plugin/public';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { LensWrapper } from '../../../../../../common/visualizations/lens/lens_wrapper';
|
||||
import { LensWrapper } from '../../../../../../components/lens';
|
||||
import { useLensAttributes, Layer } from '../../../../../../hooks/use_lens_attributes';
|
||||
import { useMetricsDataViewContext } from '../../../hooks/use_data_view';
|
||||
import { useUnifiedSearchContext } from '../../../hooks/use_unified_search';
|
||||
|
@ -130,7 +130,7 @@ export const MetricChart = ({ id, title, layers, overrides }: MetricChartProps)
|
|||
</EuiFlexGroup>
|
||||
) : (
|
||||
<LensWrapper
|
||||
id={`hostsViewsmetricsChart-${id}`}
|
||||
id={`hostsViewMetricsChart-${id}`}
|
||||
attributes={attributes}
|
||||
style={lensStyle}
|
||||
extraActions={extraActions}
|
||||
|
@ -141,7 +141,6 @@ export const MetricChart = ({ id, title, layers, overrides }: MetricChartProps)
|
|||
onBrushEnd={handleBrushEnd}
|
||||
loading={loading}
|
||||
overrides={overrides}
|
||||
hasTitle
|
||||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
|
|
|
@ -10,7 +10,7 @@ import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { hostLensFormulas, type XYLayerOptions } from '../../../../../../common/visualizations';
|
||||
import { HostMetricsDocsLink } from '../../../../../../common/visualizations/metric_explanation/host_metrics_docs_link';
|
||||
import { HostMetricsDocsLink } from '../../../../../../components/lens';
|
||||
import { MetricChart, MetricChartProps } from './metric_chart';
|
||||
|
||||
const DEFAULT_BREAKDOWN_SIZE = 20;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue