[AO] Fix overview alerts loading state (#150868)

Fixes #140387

## Summary

Fix alerts table lazy loading state

Before

![image](https://user-images.githubusercontent.com/12370520/218094792-24d00334-5414-4737-8f64-d619cbcc9f12.png)

After

![image](https://user-images.githubusercontent.com/12370520/218094828-6b3559ce-1f7a-45a7-ae0d-96f5dad141a1.png)
This commit is contained in:
Maryam Saeidi 2023-02-14 13:35:40 +01:00 committed by GitHub
parent cf94e1a288
commit 50b83014a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 83 additions and 49 deletions

View file

@ -218,9 +218,10 @@ export function OverviewPage() {
<AlertsStateTable <AlertsStateTable
alertsTableConfigurationRegistry={alertsTableConfigurationRegistry} alertsTableConfigurationRegistry={alertsTableConfigurationRegistry}
configurationId={AlertConsumers.OBSERVABILITY} configurationId={AlertConsumers.OBSERVABILITY}
id={ALERTS_TABLE_ID}
flyoutSize="s" flyoutSize="s"
featureIds={observabilityAlertFeatureIds} featureIds={observabilityAlertFeatureIds}
hideLazyLoader
id={ALERTS_TABLE_ID}
pageSize={ALERTS_PER_PAGE} pageSize={ALERTS_PER_PAGE}
query={esQuery} query={esQuery}
showExpandToDetails={false} showExpandToDetails={false}

View file

@ -5,14 +5,14 @@
* 2.0. * 2.0.
*/ */
import { EuiLoadingChart } from '@elastic/eui';
import React from 'react'; import React from 'react';
import { useLoadAlertSummary } from '../../hooks/use_load_alert_summary'; import { useLoadAlertSummary } from '../../hooks/use_load_alert_summary';
import { AlertSummaryWidgetProps } from '.'; import { AlertSummaryWidgetProps } from '.';
import { import {
AlertSummaryWidgetError, AlertSummaryWidgetError,
AlertsSummaryWidgetCompact, AlertSummaryWidgetCompact,
AlertsSummaryWidgetFullSize, AlertSummaryWidgetFullSize,
AlertSummaryWidgetLoader,
} from './components'; } from './components';
export const AlertSummaryWidget = ({ export const AlertSummaryWidget = ({
@ -33,25 +33,14 @@ export const AlertSummaryWidget = ({
timeRange, timeRange,
}); });
if (isLoading) if (isLoading) return <AlertSummaryWidgetLoader fullSize={fullSize} />;
return (
<div
style={{
minHeight: fullSize ? 238 : 224,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<EuiLoadingChart size="l" data-test-subj="alertSummaryWidgetLoading" />
</div>
);
if (error) return <AlertSummaryWidgetError />; if (error) return <AlertSummaryWidgetError />;
return fullSize ? ( return fullSize ? (
// Only show full size version if there is data // Only show full size version if there is data
activeAlertCount || recoveredAlertCount ? ( activeAlertCount || recoveredAlertCount ? (
<AlertsSummaryWidgetFullSize <AlertSummaryWidgetFullSize
activeAlertCount={activeAlertCount} activeAlertCount={activeAlertCount}
activeAlerts={activeAlerts} activeAlerts={activeAlerts}
recoveredAlertCount={recoveredAlertCount} recoveredAlertCount={recoveredAlertCount}
@ -60,7 +49,7 @@ export const AlertSummaryWidget = ({
/> />
) : null ) : null
) : ( ) : (
<AlertsSummaryWidgetCompact <AlertSummaryWidgetCompact
activeAlertCount={activeAlertCount} activeAlertCount={activeAlertCount}
activeAlerts={activeAlerts} activeAlerts={activeAlerts}
onClick={onClick} onClick={onClick}

View file

@ -6,7 +6,7 @@
*/ */
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { AlertsSummaryWidgetCompact as Component } from './alert_summary_widget_compact'; import { AlertSummaryWidgetCompact as Component } from './alert_summary_widget_compact';
import { mockedAlertSummaryResponse, mockedChartThemes } from '../../../mock/alert_summary_widget'; import { mockedAlertSummaryResponse, mockedChartThemes } from '../../../mock/alert_summary_widget';
export default { export default {

View file

@ -8,17 +8,17 @@
import React from 'react'; import React from 'react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { import {
AlertsSummaryWidgetCompact, AlertSummaryWidgetCompact,
AlertsSummaryWidgetCompactProps, AlertSummaryWidgetCompactProps,
} from './alert_summary_widget_compact'; } from './alert_summary_widget_compact';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import { mockedAlertSummaryResponse, mockedChartThemes } from '../../../mock/alert_summary_widget'; import { mockedAlertSummaryResponse, mockedChartThemes } from '../../../mock/alert_summary_widget';
describe('AlertsSummaryWidgetCompact', () => { describe('AlertSummaryWidgetCompact', () => {
const renderComponent = (props: Partial<AlertsSummaryWidgetCompactProps> = {}) => const renderComponent = (props: Partial<AlertSummaryWidgetCompactProps> = {}) =>
render( render(
<IntlProvider locale="en"> <IntlProvider locale="en">
<AlertsSummaryWidgetCompact <AlertSummaryWidgetCompact
chartThemes={mockedChartThemes} chartThemes={mockedChartThemes}
onClick={jest.fn} onClick={jest.fn}
{...mockedAlertSummaryResponse} {...mockedAlertSummaryResponse}
@ -27,7 +27,7 @@ describe('AlertsSummaryWidgetCompact', () => {
</IntlProvider> </IntlProvider>
); );
it('should render AlertsSummaryWidgetCompact', async () => { it('should render AlertSummaryWidgetCompact', async () => {
const alertSummaryWidget = renderComponent(); const alertSummaryWidget = renderComponent();
expect(alertSummaryWidget.queryByTestId('alertSummaryWidgetCompact')).toBeTruthy(); expect(alertSummaryWidget.queryByTestId('alertSummaryWidgetCompact')).toBeTruthy();

View file

@ -14,7 +14,7 @@ import { AlertCounts } from './alert_counts';
import { ALL_ALERT_COLOR, WIDGET_TITLE } from './constants'; import { ALL_ALERT_COLOR, WIDGET_TITLE } from './constants';
import { Alert, ChartThemes } from '../types'; import { Alert, ChartThemes } from '../types';
export interface AlertsSummaryWidgetCompactProps { export interface AlertSummaryWidgetCompactProps {
activeAlertCount: number; activeAlertCount: number;
activeAlerts: Alert[]; activeAlerts: Alert[];
chartThemes: ChartThemes; chartThemes: ChartThemes;
@ -23,14 +23,14 @@ export interface AlertsSummaryWidgetCompactProps {
onClick: (status?: AlertStatus) => void; onClick: (status?: AlertStatus) => void;
} }
export const AlertsSummaryWidgetCompact = ({ export const AlertSummaryWidgetCompact = ({
activeAlertCount, activeAlertCount,
activeAlerts, activeAlerts,
chartThemes: { theme, baseTheme }, chartThemes: { theme, baseTheme },
recoveredAlertCount, recoveredAlertCount,
timeRangeTitle, timeRangeTitle,
onClick, onClick,
}: AlertsSummaryWidgetCompactProps) => { }: AlertSummaryWidgetCompactProps) => {
const chartTheme = [ const chartTheme = [
theme, theme,
EUI_SPARKLINE_THEME_PARTIAL, EUI_SPARKLINE_THEME_PARTIAL,

View file

@ -5,7 +5,7 @@
* 2.0. * 2.0.
*/ */
import { AlertsSummaryWidgetFullSize as Component } from './alert_summary_widget_full_size'; import { AlertSummaryWidgetFullSize as Component } from './alert_summary_widget_full_size';
import { mockedAlertSummaryResponse, mockedChartThemes } from '../../../mock/alert_summary_widget'; import { mockedAlertSummaryResponse, mockedChartThemes } from '../../../mock/alert_summary_widget';
export default { export default {

View file

@ -8,17 +8,17 @@
import React from 'react'; import React from 'react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { import {
AlertsSummaryWidgetFullSize, AlertSummaryWidgetFullSize,
AlertsSummaryWidgetFullSizeProps, AlertSummaryWidgetFullSizeProps,
} from './alert_summary_widget_full_size'; } from './alert_summary_widget_full_size';
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import { mockedAlertSummaryResponse, mockedChartThemes } from '../../../mock/alert_summary_widget'; import { mockedAlertSummaryResponse, mockedChartThemes } from '../../../mock/alert_summary_widget';
describe('AlertSummaryWidgetFullSize', () => { describe('AlertSummaryWidgetFullSize', () => {
const renderComponent = (props: Partial<AlertsSummaryWidgetFullSizeProps> = {}) => const renderComponent = (props: Partial<AlertSummaryWidgetFullSizeProps> = {}) =>
render( render(
<IntlProvider locale="en"> <IntlProvider locale="en">
<AlertsSummaryWidgetFullSize <AlertSummaryWidgetFullSize
chartThemes={mockedChartThemes} chartThemes={mockedChartThemes}
{...mockedAlertSummaryResponse} {...mockedAlertSummaryResponse}
{...props} {...props}

View file

@ -13,7 +13,7 @@ import { AlertCounts } from './alert_counts';
import { ALL_ALERT_COLOR, TOOLTIP_DATE_FORMAT } from './constants'; import { ALL_ALERT_COLOR, TOOLTIP_DATE_FORMAT } from './constants';
import { Alert, ChartThemes } from '../types'; import { Alert, ChartThemes } from '../types';
export interface AlertsSummaryWidgetFullSizeProps { export interface AlertSummaryWidgetFullSizeProps {
activeAlertCount: number; activeAlertCount: number;
activeAlerts: Alert[]; activeAlerts: Alert[];
chartThemes: ChartThemes; chartThemes: ChartThemes;
@ -21,13 +21,13 @@ export interface AlertsSummaryWidgetFullSizeProps {
dateFormat?: string; dateFormat?: string;
} }
export const AlertsSummaryWidgetFullSize = ({ export const AlertSummaryWidgetFullSize = ({
activeAlertCount, activeAlertCount,
activeAlerts, activeAlerts,
chartThemes: { theme, baseTheme }, chartThemes: { theme, baseTheme },
dateFormat, dateFormat,
recoveredAlertCount, recoveredAlertCount,
}: AlertsSummaryWidgetFullSizeProps) => { }: AlertSummaryWidgetFullSizeProps) => {
const chartTheme = [ const chartTheme = [
theme, theme,
{ {

View file

@ -0,0 +1,27 @@
/*
* 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 { EuiLoadingChart } from '@elastic/eui';
import { AlertSummaryWidgetProps } from '..';
type Props = Pick<AlertSummaryWidgetProps, 'fullSize'>;
export const AlertSummaryWidgetLoader = ({ fullSize }: Props) => {
return (
<div
style={{
minHeight: fullSize ? 238 : 224,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<EuiLoadingChart size="l" data-test-subj="alertSummaryWidgetLoading" />
</div>
);
};

View file

@ -5,6 +5,7 @@
* 2.0. * 2.0.
*/ */
export { AlertsSummaryWidgetCompact } from './alert_summary_widget_compact'; export { AlertSummaryWidgetCompact } from './alert_summary_widget_compact';
export { AlertSummaryWidgetError } from './alert_summary_widget_error'; export { AlertSummaryWidgetError } from './alert_summary_widget_error';
export { AlertsSummaryWidgetFullSize } from './alert_summary_widget_full_size'; export { AlertSummaryWidgetFullSize } from './alert_summary_widget_full_size';
export { AlertSummaryWidgetLoader } from './alert_summary_widget_loader';

View file

@ -58,9 +58,6 @@ export const RuleDefinition = suspendedComponentWithProps(
export const RuleTagBadge = suspendedComponentWithProps( export const RuleTagBadge = suspendedComponentWithProps(
lazy(() => import('./rules_list/components/rule_tag_badge')) lazy(() => import('./rules_list/components/rule_tag_badge'))
); );
export const AlertSummaryWidget = suspendedComponentWithProps(
lazy(() => import('./alert_summary_widget/alert_summary_widget'))
);
export const RuleStatusPanel = suspendedComponentWithProps( export const RuleStatusPanel = suspendedComponentWithProps(
lazy(() => import('./rule_details/components/rule_status_panel')) lazy(() => import('./rule_details/components/rule_status_panel'))
); );

View file

@ -7,6 +7,7 @@
import { EuiLoadingSpinner } from '@elastic/eui'; import { EuiLoadingSpinner } from '@elastic/eui';
import React, { lazy, Suspense } from 'react'; import React, { lazy, Suspense } from 'react';
import { LazyLoadProps } from '../types';
import type { AlertsTableStateProps } from '../application/sections/alerts_table/alerts_table_state'; import type { AlertsTableStateProps } from '../application/sections/alerts_table/alerts_table_state';
@ -14,8 +15,11 @@ const AlertsTableStateLazy: React.FC<AlertsTableStateProps> = lazy(
() => import('../application/sections/alerts_table/alerts_table_state') () => import('../application/sections/alerts_table/alerts_table_state')
); );
export const getAlertsTableStateLazy = (props: AlertsTableStateProps) => ( export const getAlertsTableStateLazy = ({
<Suspense fallback={<EuiLoadingSpinner />}> hideLazyLoader,
...props
}: AlertsTableStateProps & LazyLoadProps) => (
<Suspense fallback={hideLazyLoader ? null : <EuiLoadingSpinner />}>
<AlertsTableStateLazy {...props} /> <AlertsTableStateLazy {...props} />
</Suspense> </Suspense>
); );

View file

@ -5,10 +5,18 @@
* 2.0. * 2.0.
*/ */
import React from 'react'; import React, { lazy, Suspense } from 'react';
import { AlertSummaryWidget } from '../application/sections'; import { AlertSummaryWidgetLoader } from '../application/sections/alert_summary_widget/components';
import { AlertSummaryWidgetProps } from '../application/sections/alert_summary_widget'; import { AlertSummaryWidgetProps } from '../application/sections/alert_summary_widget';
const AlertSummaryWidgetLazy: React.FC<AlertSummaryWidgetProps> = lazy(
() => import('../application/sections/alert_summary_widget/alert_summary_widget')
);
export const getAlertSummaryWidgetLazy = (props: AlertSummaryWidgetProps) => { export const getAlertSummaryWidgetLazy = (props: AlertSummaryWidgetProps) => {
return <AlertSummaryWidget {...props} />; return (
<Suspense fallback={<AlertSummaryWidgetLoader fullSize={props.fullSize} />}>
<AlertSummaryWidgetLazy {...props} />
</Suspense>
);
}; };

View file

@ -47,6 +47,7 @@ import {
ExperimentalFeatures, ExperimentalFeatures,
parseExperimentalConfigValue, parseExperimentalConfigValue,
} from '../common/experimental_features'; } from '../common/experimental_features';
import { LazyLoadProps } from './types';
import type { import type {
ActionTypeModel, ActionTypeModel,
@ -111,7 +112,9 @@ export interface TriggersAndActionsUIPublicPluginStart {
props: Omit<RuleEditProps, 'actionTypeRegistry' | 'ruleTypeRegistry'> props: Omit<RuleEditProps, 'actionTypeRegistry' | 'ruleTypeRegistry'>
) => ReactElement<RuleEditProps>; ) => ReactElement<RuleEditProps>;
getAlertsTable: (props: AlertsTableProps) => ReactElement<AlertsTableProps>; getAlertsTable: (props: AlertsTableProps) => ReactElement<AlertsTableProps>;
getAlertsStateTable: (props: AlertsTableStateProps) => ReactElement<AlertsTableStateProps>; getAlertsStateTable: (
props: AlertsTableStateProps & LazyLoadProps
) => ReactElement<AlertsTableStateProps>;
getAlertsSearchBar: (props: AlertsSearchBarProps) => ReactElement<AlertsSearchBarProps>; getAlertsSearchBar: (props: AlertsSearchBarProps) => ReactElement<AlertsSearchBarProps>;
getFieldBrowser: (props: FieldBrowserProps) => ReactElement<FieldBrowserProps>; getFieldBrowser: (props: FieldBrowserProps) => ReactElement<FieldBrowserProps>;
getRuleStatusDropdown: (props: RuleStatusDropdownProps) => ReactElement<RuleStatusDropdownProps>; getRuleStatusDropdown: (props: RuleStatusDropdownProps) => ReactElement<RuleStatusDropdownProps>;
@ -381,7 +384,7 @@ export class Plugin
connectorServices: this.connectorServices!, connectorServices: this.connectorServices!,
}); });
}, },
getAlertsStateTable: (props: AlertsTableStateProps) => { getAlertsStateTable: (props: AlertsTableStateProps & LazyLoadProps) => {
return getAlertsTableStateLazy(props); return getAlertsTableStateLazy(props);
}, },
getAlertsSearchBar: (props: AlertsSearchBarProps) => { getAlertsSearchBar: (props: AlertsSearchBarProps) => {

View file

@ -666,3 +666,7 @@ export interface UpdateRulesToBulkEditProps {
rules?: RuleTableItem[]; rules?: RuleTableItem[];
filter?: KueryNode | null; filter?: KueryNode | null;
} }
export interface LazyLoadProps {
hideLazyLoader?: boolean;
}