Skip to content

Commit

Permalink
[Infra UI] Fix page content reload when date picker has relative dates (
Browse files Browse the repository at this point in the history
#165462)

closes #165194

## Summary

This PR fixes the problem with the Asset Details not reloading its
content when date-picker had relative dates.

**flyout**


https://github.com/elastic/kibana/assets/2767137/3823f587-c743-41d7-aa05-75b3e072ef4c


**full-page**


https://github.com/elastic/kibana/assets/2767137/4a246ddd-6b7e-4650-834b-97d2db0740d5



I've also changed all the charts to receive absolute date ranges. If
they used relative dates, `now` will be calculated when each chart
starts loading, this could change significantly if there is a delay
between loading each individual chart.

### How to test
- Start a local Kibana instance
- Navigate to Infrastructure > Hosts
- Open the flyout, change the date picker's end-date to `now` and submit
(do that one more time)
- Open the full page, change the date picker's end-date to `now` and
submit (do that one more time)

---------

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
crespocarlos and kibanamachine authored Sep 7, 2023
1 parent 28c72c4 commit a057a88
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { memoryUsage, memoryUsageBreakdown } from '../metric_charts/memory';
import { rxTx } from '../metric_charts/network';
import type { XYConfig } from '../metric_charts/types';

export const hostMetricCharts: XYConfig[] = [
export const hostMetricFlyoutCharts: XYConfig[] = [
cpuUsage,
memoryUsage,
normalizedLoad1m,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
* 2.0.
*/

import { hostMetricCharts, hostMetricChartsFullPage } from './host/host_metric_charts';
import { hostMetricFlyoutCharts, hostMetricChartsFullPage } from './host/host_metric_charts';
import { hostKPICharts, KPIChartProps } from './host/host_kpi_charts';
import { nginxAccessCharts, nginxStubstatusCharts } from './host/nginx_charts';

export { type KPIChartProps };
export const assetDetailsDashboards = {
host: { hostMetricCharts, hostMetricChartsFullPage, hostKPICharts },
nginxDashboard: { nginxStubstatusCharts, nginxAccessCharts },
host: { hostMetricFlyoutCharts, hostMetricChartsFullPage, hostKPICharts },
nginx: { nginxStubstatusCharts, nginxAccessCharts },
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,66 @@

import type { TimeRange } from '@kbn/es-query';
import createContainer from 'constate';
import { useCallback, useMemo } from 'react';
import { useCallback, useState } from 'react';
import useEffectOnce from 'react-use/lib/useEffectOnce';
import { parseDateRange } from '../../../utils/datemath';

import { toTimestampRange } from '../utils';
import { useAssetDetailsUrlState } from './use_asset_details_url_state';

const DEFAULT_DATE_RANGE: TimeRange = {
from: 'now-15m',
to: 'now',
};

export interface UseDateRangeProviderProps {
initialDateRange: TimeRange;
}

const DEFAULT_FROM_IN_MILLISECONDS = 15 * 60000;
const getDefaultDateRange = () => {
const now = Date.now();

return {
from: new Date(now - DEFAULT_FROM_IN_MILLISECONDS).toISOString(),
to: new Date(now).toISOString(),
};
};

export function useDateRangeProvider({ initialDateRange }: UseDateRangeProviderProps) {
const [urlState, setUrlState] = useAssetDetailsUrlState();
const dateRange: TimeRange = urlState?.dateRange ?? initialDateRange;
const [parsedDateRange, setParsedDateRange] = useState(parseDateRange(dateRange));
const [refreshTs, setRefreshTs] = useState(Date.now());

useEffectOnce(() => {
const { from, to } = getParsedDateRange();

// forces the date picker to initiallize with absolute dates.
setUrlState({ dateRange: { from, to } });
});

const setDateRange = useCallback(
(newDateRange: TimeRange) => {
setUrlState({ dateRange: newDateRange });
setParsedDateRange(parseDateRange(newDateRange));
setRefreshTs(Date.now());
},
[setUrlState]
);

const parsedDateRange = useMemo(() => {
const { from = DEFAULT_DATE_RANGE.from, to = DEFAULT_DATE_RANGE.to } =
parseDateRange(dateRange);
const getParsedDateRange = useCallback(() => {
const defaultDateRange = getDefaultDateRange();
const { from = defaultDateRange.from, to = defaultDateRange.to } = parsedDateRange;

return { from, to };
}, [dateRange]);
}, [parsedDateRange]);

const getDateRangeInTimestamp = useCallback(
() => toTimestampRange(parsedDateRange),
[parsedDateRange]
);
const getDateRangeInTimestamp = useCallback(() => {
return toTimestampRange(getParsedDateRange());
}, [getParsedDateRange]);

return { dateRange, setDateRange, getDateRangeInTimestamp };
return {
dateRange,
getDateRangeInTimestamp,
getParsedDateRange,
refreshTs,
setDateRange,
};
}

export const [DateRangeProvider, useDateRangeProviderContext] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
KPI_CHART_HEIGHT,
AVERAGE_SUBTITLE,
} from '../../../../../common/visualizations';
import { useDateRangeProviderContext } from '../../../hooks/use_date_range';

interface Props {
dataView?: DataView;
Expand All @@ -24,6 +25,7 @@ interface Props {
}

export const KPIGrid = React.memo(({ nodeName, dataView, timeRange }: Props) => {
const { refreshTs } = useDateRangeProviderContext();
const filters = useMemo(() => {
return [
buildCombinedHostsFilter({
Expand All @@ -43,6 +45,7 @@ export const KPIGrid = React.memo(({ nodeName, dataView, timeRange }: Props) =>
dataView={dataView}
dateRange={timeRange}
layers={{ ...layers, options: { ...layers.options, subtitle: AVERAGE_SUBTITLE } }}
lastReloadRequestTime={refreshTs}
height={KPI_CHART_HEIGHT}
filters={filters}
title={title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,22 @@ import { MetricsSectionTitle, NginxMetricsSectionTitle } from '../../../componen

interface Props {
assetName: string;
timeRange: TimeRange;
dateRange: TimeRange;
metricsDataView?: DataView;
logsDataView?: DataView;
}

const { host, nginx } = assetDetailsDashboards;

export const MetricsGrid = React.memo(
({ assetName, metricsDataView, logsDataView, timeRange }: Props) => {
({ assetName, metricsDataView, logsDataView, dateRange: timeRange }: Props) => {
return (
<>
<Section title={MetricsSectionTitle}>
<ChartGrid
assetName={assetName}
timeRange={timeRange}
charts={assetDetailsDashboards.host.hostMetricChartsFullPage}
charts={host.hostMetricChartsFullPage}
metricsDataView={metricsDataView}
logsDataView={logsDataView}
data-test-subj="infraAssetDetailsMetricsChart"
Expand All @@ -40,12 +42,12 @@ export const MetricsGrid = React.memo(
assetName={assetName}
timeRange={timeRange}
charts={[
...assetDetailsDashboards.nginxDashboard.nginxStubstatusCharts.map((n) => ({
...n,
...nginx.nginxStubstatusCharts.map((chart) => ({
...chart,
dependsOn: ['nginx.stubstatus'],
})),
...assetDetailsDashboards.nginxDashboard.nginxAccessCharts.map((n) => ({
...n,
...nginx.nginxAccessCharts.map((chart) => ({
...chart,
dependsOn: ['nginx.access'],
})),
]}
Expand All @@ -63,13 +65,13 @@ export const MetricsGridCompact = ({
assetName,
metricsDataView,
logsDataView,
timeRange,
dateRange: timeRange,
}: Props) => (
<Section title={MetricsSectionTitle}>
<ChartGrid
assetName={assetName}
timeRange={timeRange}
charts={assetDetailsDashboards.host.hostMetricCharts}
charts={host.hostMetricFlyoutCharts}
metricsDataView={metricsDataView}
logsDataView={logsDataView}
data-test-subj="infraAssetDetailsMetricsChart"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { useDateRangeProviderContext } from '../../hooks/use_date_range';
import { SectionSeparator } from './section_separator';

export const Overview = () => {
const { dateRange } = useDateRangeProviderContext();
const { getParsedDateRange } = useDateRangeProviderContext();
const { asset, assetType, renderMode } = useAssetDetailsRenderPropsContext();
const {
metadata,
Expand All @@ -32,18 +32,19 @@ export const Overview = () => {
} = useMetadataStateProviderContext();
const { logs, metrics } = useDataViewsProviderContext();

const parsedDateRange = getParsedDateRange();
const isFullPageView = renderMode.mode !== 'flyout';

const metricsSection = isFullPageView ? (
<MetricsGrid
timeRange={dateRange}
dateRange={parsedDateRange}
logsDataView={logs.dataView}
metricsDataView={metrics.dataView}
assetName={asset.name}
/>
) : (
<MetricsGridCompact
timeRange={dateRange}
dateRange={parsedDateRange}
logsDataView={logs.dataView}
metricsDataView={metrics.dataView}
assetName={asset.name}
Expand All @@ -58,7 +59,7 @@ export const Overview = () => {
return (
<EuiFlexGroup direction="column" gutterSize="m">
<EuiFlexItem grow={false}>
<KPIGrid nodeName={asset.name} timeRange={dateRange} dataView={metrics.dataView} />
<KPIGrid nodeName={asset.name} timeRange={parsedDateRange} dataView={metrics.dataView} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
{fetchMetadataError ? (
Expand Down Expand Up @@ -88,12 +89,16 @@ export const Overview = () => {
/>
</EuiCallOut>
) : (
<>{metadataSummarySection}</>
metadataSummarySection
)}
<SectionSeparator />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AlertsSummaryContent assetName={asset.name} assetType={assetType} dateRange={dateRange} />
<AlertsSummaryContent
assetName={asset.name}
assetType={assetType}
dateRange={parsedDateRange}
/>
<SectionSeparator />
</EuiFlexItem>
<EuiFlexItem grow={false}>{metricsSection}</EuiFlexItem>
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/infra/public/components/lens/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ export { ChartPlaceholder } from './chart_placeholder';
export { TooltipContent } from './metric_explanation/tooltip_content';
export { HostMetricsDocsLink } from './metric_explanation/host_metrics_docs_link';
export { HostMetricsExplanationContent } from './metric_explanation/host_metrics_explanation_content';

export * from './types';
39 changes: 17 additions & 22 deletions x-pack/plugins/infra/public/components/lens/lens_chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,43 +56,38 @@ export const LensChart = ({

const sytle: CSSProperties = useMemo(() => ({ height }), [height]);

const Lens = (
const lens = (
<LensWrapper
id={id}
attributes={attributes}
dateRange={dateRange}
disableTriggers={disableTriggers}
extraActions={extraActions}
filters={filters}
hidePanelTitles={hidePanelTitles}
lastReloadRequestTime={lastReloadRequestTime}
loading={isLoading}
style={sytle}
query={query}
overrides={overrides}
onBrushEnd={onBrushEnd}
hidePanelTitles={hidePanelTitles}
/>
);

const getContent = () => {
if (!toolTip) {
return Lens;
}

return (
<EuiToolTip
delay="regular"
content={React.cloneElement(toolTip, {
formula,
})}
anchorClassName="eui-fullWidth"
>
{/* EuiToolTip forwards some event handlers to the child component.
const content = !toolTip ? (
lens
) : (
<EuiToolTip
delay="regular"
content={React.cloneElement(toolTip, {
formula,
})}
anchorClassName="eui-fullWidth"
>
{/* EuiToolTip forwards some event handlers to the child component.
Wrapping Lens inside a div prevents that from causing unnecessary re-renders */}
<div>{Lens}</div>
</EuiToolTip>
);
};
<div>{lens}</div>
</EuiToolTip>
);

return (
<EuiPanel
Expand All @@ -106,7 +101,7 @@ export const LensChart = ({
min-height: ${height}px;
`}
>
{error ? <ChartLoadError /> : getContent()}
{error ? <ChartLoadError /> : content}
</EuiPanel>
);
};
33 changes: 15 additions & 18 deletions x-pack/plugins/infra/public/components/lens/lens_wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 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';
Expand All @@ -15,23 +15,15 @@ import { LensAttributes } from '@kbn/lens-embeddable-utils';
import { useKibanaContextForPlugin } from '../../hooks/use_kibana';
import { ChartLoadingProgress, ChartPlaceholder } from './chart_placeholder';
import { parseDateRange } from '../../utils/datemath';

export type LensWrapperProps = Omit<
TypedLensByValueInput,
'timeRange' | 'attributes' | 'viewMode'
> & {
attributes: LensAttributes | null;
dateRange: TimeRange;
extraActions: Action[];
loading?: boolean;
};
import type { LensWrapperProps } from './types';

export const LensWrapper = ({
attributes,
dateRange,
filters,
lastReloadRequestTime,
loading = false,
onLoad,
query,
...props
}: LensWrapperProps) => {
Expand Down Expand Up @@ -85,11 +77,17 @@ export const LensWrapper = ({
query,
]);

const onLoad = useCallback(() => {
if (!embeddableLoaded) {
setEmbeddableLoaded(true);
}
}, [embeddableLoaded]);
const handleOnLoad = useCallback(
(isLoading: boolean) => {
if (!embeddableLoaded) {
setEmbeddableLoaded(true);
}
if (onLoad) {
onLoad(isLoading);
}
},
[embeddableLoaded, onLoad]
);

const parsedDateRange: TimeRange = useMemo(() => {
const { from = state.dateRange.from, to = state.dateRange.to } = parseDateRange(
Expand All @@ -104,7 +102,6 @@ export const LensWrapper = ({
<div
css={css`
position: relative;
border-radius: ${euiTheme.size.s};
overflow: hidden;
height: 100%;
.echMetric {
Expand All @@ -125,7 +122,7 @@ export const LensWrapper = ({
attributes={state.attributes}
filters={state.filters}
lastReloadRequestTime={state.lastReloadRequestTime}
onLoad={onLoad}
onLoad={handleOnLoad}
query={state.query}
timeRange={parsedDateRange}
viewMode={ViewMode.VIEW}
Expand Down
Loading

0 comments on commit a057a88

Please sign in to comment.