move useSavedView context changes to outer layout container (#99411)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Sandra Gonzales 2021-05-11 12:32:17 -04:00 committed by GitHub
parent 9b640f3308
commit 82ab5c50a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 188 additions and 163 deletions

View file

@ -9,6 +9,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import useInterval from 'react-use/lib/useInterval';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { SavedView } from '../../../../containers/saved_view/saved_view';
import { AutoSizer } from '../../../../components/auto_sizer';
import { convertIntervalToString } from '../../../../utils/convert_interval_to_string';
import { NodesOverview } from './nodes_overview';
@ -26,182 +27,191 @@ import { ViewSwitcher } from './waffle/view_switcher';
import { IntervalLabel } from './waffle/interval_label';
import { createInventoryMetricFormatter } from '../lib/create_inventory_metric_formatter';
import { createLegend } from '../lib/create_legend';
import { useSavedViewContext } from '../../../../containers/saved_view/saved_view';
import { useWaffleViewState } from '../hooks/use_waffle_view_state';
import { SavedViewsToolbarControls } from '../../../../components/saved_views/toolbar_control';
import { BottomDrawer } from './bottom_drawer';
import { Legend } from './waffle/legend';
export const Layout = () => {
const [showLoading, setShowLoading] = useState(true);
const { sourceId, source } = useSourceContext();
const { currentView, shouldLoadDefault } = useSavedViewContext();
const {
metric,
groupBy,
sort,
nodeType,
accountId,
region,
changeView,
view,
autoBounds,
boundsOverride,
legend,
} = useWaffleOptionsContext();
const { currentTime, jumpToTime, isAutoReloading } = useWaffleTimeContext();
const { filterQueryAsJson, applyFilterQuery } = useWaffleFiltersContext();
const { loading, nodes, reload, interval } = useSnapshot(
filterQueryAsJson,
[metric],
groupBy,
nodeType,
sourceId,
currentTime,
accountId,
region,
false
);
export const Layout = React.memo(
({
shouldLoadDefault,
currentView,
}: {
shouldLoadDefault: boolean;
currentView: SavedView<any> | null;
}) => {
const [showLoading, setShowLoading] = useState(true);
const { sourceId, source } = useSourceContext();
const {
metric,
groupBy,
sort,
nodeType,
accountId,
region,
changeView,
view,
autoBounds,
boundsOverride,
legend,
} = useWaffleOptionsContext();
const { currentTime, jumpToTime, isAutoReloading } = useWaffleTimeContext();
const { filterQueryAsJson, applyFilterQuery } = useWaffleFiltersContext();
const { loading, nodes, reload, interval } = useSnapshot(
filterQueryAsJson,
[metric],
groupBy,
nodeType,
sourceId,
currentTime,
accountId,
region,
false
);
const legendPalette = legend?.palette ?? DEFAULT_LEGEND.palette;
const legendSteps = legend?.steps ?? DEFAULT_LEGEND.steps;
const legendReverseColors = legend?.reverseColors ?? DEFAULT_LEGEND.reverseColors;
const legendPalette = legend?.palette ?? DEFAULT_LEGEND.palette;
const legendSteps = legend?.steps ?? DEFAULT_LEGEND.steps;
const legendReverseColors = legend?.reverseColors ?? DEFAULT_LEGEND.reverseColors;
const options = {
formatter: InfraFormatterType.percent,
formatTemplate: '{{value}}',
legend: createLegend(legendPalette, legendSteps, legendReverseColors),
metric,
sort,
fields: source?.configuration?.fields,
groupBy,
};
const options = {
formatter: InfraFormatterType.percent,
formatTemplate: '{{value}}',
legend: createLegend(legendPalette, legendSteps, legendReverseColors),
metric,
sort,
fields: source?.configuration?.fields,
groupBy,
};
useInterval(
() => {
if (!loading) {
jumpToTime(Date.now());
}
},
isAutoReloading ? 5000 : null
);
useInterval(
() => {
if (!loading) {
jumpToTime(Date.now());
}
},
isAutoReloading ? 5000 : null
);
const intervalAsString = convertIntervalToString(interval);
const dataBounds = calculateBoundsFromNodes(nodes);
const bounds = autoBounds ? dataBounds : boundsOverride;
/* eslint-disable-next-line react-hooks/exhaustive-deps */
const formatter = useCallback(createInventoryMetricFormatter(options.metric), [options.metric]);
const { viewState, onViewChange } = useWaffleViewState();
useEffect(() => {
if (currentView) {
onViewChange(currentView);
}
}, [currentView, onViewChange]);
useEffect(() => {
// load snapshot data after default view loaded, unless we're not loading a view
if (currentView != null || !shouldLoadDefault) {
reload();
}
/**
* INFO: why disable exhaustive-deps
* We need to wait on the currentView not to be null because it is loaded async and could change the view state.
* We don't actually need to watch the value of currentView though, since the view state will be synched up by the
* changing params in the reload method so we should only "watch" the reload method.
*
* TODO: Should refactor this in the future to make it more clear where all the view state is coming
* from and it's precedence [query params, localStorage, defaultView, out of the box view]
*/
const intervalAsString = convertIntervalToString(interval);
const dataBounds = calculateBoundsFromNodes(nodes);
const bounds = autoBounds ? dataBounds : boundsOverride;
/* eslint-disable-next-line react-hooks/exhaustive-deps */
}, [reload, shouldLoadDefault]);
const formatter = useCallback(createInventoryMetricFormatter(options.metric), [options.metric]);
const { viewState, onViewChange } = useWaffleViewState();
useEffect(() => {
setShowLoading(true);
}, [options.metric, nodeType]);
useEffect(() => {
if (currentView) {
onViewChange(currentView);
}
}, [currentView, onViewChange]);
useEffect(() => {
const hasNodes = nodes && nodes.length;
// Don't show loading screen when we're auto-reloading
setShowLoading(!hasNodes);
}, [nodes]);
useEffect(() => {
// load snapshot data after default view loaded, unless we're not loading a view
if (currentView != null || !shouldLoadDefault) {
reload();
}
return (
<>
<PageContent>
<AutoSizer bounds>
{({ measureRef: pageMeasureRef, bounds: { width = 0 } }) => (
<MainContainer ref={pageMeasureRef}>
<AutoSizer bounds>
{({ measureRef: topActionMeasureRef, bounds: { height: topActionHeight = 0 } }) => (
<>
<TopActionContainer ref={topActionMeasureRef}>
<EuiFlexGroup
justifyContent="spaceBetween"
alignItems="center"
gutterSize="m"
>
<Toolbar nodeType={nodeType} currentTime={currentTime} />
<EuiFlexItem grow={false}>
<IntervalLabel intervalAsString={intervalAsString} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ViewSwitcher view={view} onChange={changeView} />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<SavedViewContainer>
<SavedViewsToolbarControls viewState={viewState} />
</SavedViewContainer>
</TopActionContainer>
<AutoSizer bounds>
{({ measureRef, bounds: { height = 0 } }) => (
<>
<NodesOverview
nodes={nodes}
options={options}
nodeType={nodeType}
loading={loading}
showLoading={showLoading}
reload={reload}
onDrilldown={applyFilterQuery}
currentTime={currentTime}
view={view}
autoBounds={autoBounds}
boundsOverride={boundsOverride}
formatter={formatter}
bottomMargin={height}
topMargin={topActionHeight}
/>
{view === 'map' && (
<BottomDrawer
measureRef={measureRef}
interval={interval}
/**
* INFO: why disable exhaustive-deps
* We need to wait on the currentView not to be null because it is loaded async and could change the view state.
* We don't actually need to watch the value of currentView though, since the view state will be synched up by the
* changing params in the reload method so we should only "watch" the reload method.
*
* TODO: Should refactor this in the future to make it more clear where all the view state is coming
* from and it's precedence [query params, localStorage, defaultView, out of the box view]
*/
/* eslint-disable-next-line react-hooks/exhaustive-deps */
}, [reload, shouldLoadDefault]);
useEffect(() => {
setShowLoading(true);
}, [options.metric, nodeType]);
useEffect(() => {
const hasNodes = nodes && nodes.length;
// Don't show loading screen when we're auto-reloading
setShowLoading(!hasNodes);
}, [nodes]);
return (
<>
<PageContent>
<AutoSizer bounds>
{({ measureRef: pageMeasureRef, bounds: { width = 0 } }) => (
<MainContainer ref={pageMeasureRef}>
<AutoSizer bounds>
{({
measureRef: topActionMeasureRef,
bounds: { height: topActionHeight = 0 },
}) => (
<>
<TopActionContainer ref={topActionMeasureRef}>
<EuiFlexGroup
justifyContent="spaceBetween"
alignItems="center"
gutterSize="m"
>
<Toolbar nodeType={nodeType} currentTime={currentTime} />
<EuiFlexItem grow={false}>
<IntervalLabel intervalAsString={intervalAsString} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<ViewSwitcher view={view} onChange={changeView} />
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer />
<SavedViewContainer>
<SavedViewsToolbarControls viewState={viewState} />
</SavedViewContainer>
</TopActionContainer>
<AutoSizer bounds>
{({ measureRef, bounds: { height = 0 } }) => (
<>
<NodesOverview
nodes={nodes}
options={options}
nodeType={nodeType}
loading={loading}
showLoading={showLoading}
reload={reload}
onDrilldown={applyFilterQuery}
currentTime={currentTime}
view={view}
autoBounds={autoBounds}
boundsOverride={boundsOverride}
formatter={formatter}
width={width}
>
<Legend
bottomMargin={height}
topMargin={topActionHeight}
/>
{view === 'map' && (
<BottomDrawer
measureRef={measureRef}
interval={interval}
formatter={formatter}
bounds={bounds}
dataBounds={dataBounds}
legend={options.legend}
/>
</BottomDrawer>
)}
</>
)}
</AutoSizer>
</>
)}
</AutoSizer>
</MainContainer>
)}
</AutoSizer>
</PageContent>
</>
);
};
width={width}
>
<Legend
formatter={formatter}
bounds={bounds}
dataBounds={dataBounds}
legend={options.legend}
/>
</BottomDrawer>
)}
</>
)}
</AutoSizer>
</>
)}
</AutoSizer>
</MainContainer>
)}
</AutoSizer>
</PageContent>
</>
);
}
);
const MainContainer = euiStyled.div`
position: relative;

View file

@ -0,0 +1,15 @@
/*
* 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 { useSavedViewContext } from '../../../../containers/saved_view/saved_view';
import { Layout } from './layout';
export const LayoutView = () => {
const { shouldLoadDefault, currentView } = useSavedViewContext();
return <Layout shouldLoadDefault={shouldLoadDefault} currentView={currentView} />;
};

View file

@ -21,7 +21,7 @@ import { ViewSourceConfigurationButton } from '../../../components/source_config
import { Source } from '../../../containers/metrics_source';
import { useTrackPageview } from '../../../../../observability/public';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { Layout } from './components/layout';
import { LayoutView } from './components/layout_view';
import { useLinkProps } from '../../../hooks/use_link_props';
import { SavedViewProvider } from '../../../containers/saved_view/saved_view';
import { DEFAULT_WAFFLE_VIEW_STATE } from './hooks/use_waffle_view_state';
@ -69,7 +69,7 @@ export const SnapshotPage = () => {
viewType={'inventory-view'}
defaultViewState={DEFAULT_WAFFLE_VIEW_STATE}
>
<Layout />
<LayoutView />
</SavedViewProvider>
</>
) : hasFailedLoadingSource ? (