Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ObsUx][Infra] Add collapsible sections in the overview tab #175716

Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
53507a8
Add collapsible sections in the asset details overview tab
jennypavlova Jan 25, 2024
b70bf99
Merge branch 'main' of github.com:elastic/kibana into 175558-infra-ad…
jennypavlova Jan 26, 2024
636c00e
Update section id, add closed section content and no alerts case
jennypavlova Jan 26, 2024
bccf4cd
Add tests and fix test ids
jennypavlova Jan 26, 2024
e8cdbe9
Change data test subjs
jennypavlova Jan 30, 2024
2e9dcc8
Try to use alerts count only in the alert widget and close section if…
jennypavlova Jan 30, 2024
e820931
Update ids and alerts closed content
jennypavlova Feb 1, 2024
e4d5d08
Refactoring: remove alerts customizations
jennypavlova Feb 1, 2024
d8f032b
Merge branch 'main' into 175558-infra-add-collapsible-sections
jennypavlova Feb 1, 2024
4b70650
Investigate a way to get alerts count
jennypavlova Feb 1, 2024
a739183
Merge branch '175558-infra-add-collapsible-sections' of https://githu…
jennypavlova Feb 1, 2024
b4c377e
Merge branch 'main' of github.com:elastic/kibana into 175558-infra-ad…
jennypavlova Feb 5, 2024
87d718e
Cleanup
jennypavlova Feb 5, 2024
5e3a34f
Add tests
jennypavlova Feb 5, 2024
a91846d
Merge branch 'main' into 175558-infra-add-collapsible-sections-in-the…
jennypavlova Feb 5, 2024
0c98488
Merge branch 'main' into 175558-infra-add-collapsible-sections-in-the…
jennypavlova Feb 6, 2024
24b9025
Merge branch 'main' into 175558-infra-add-collapsible-sections-in-the…
jennypavlova Feb 6, 2024
9f9edec
CR changes
jennypavlova Feb 6, 2024
aecd931
CR/Requested changes
jennypavlova Feb 8, 2024
6ee4927
Align design with apm
jennypavlova Feb 8, 2024
401bde1
CR changes
jennypavlova Feb 8, 2024
9311caf
Test id fix
jennypavlova Feb 8, 2024
4747262
Merge branch 'main' of github.com:elastic/kibana into 175558-infra-ad…
jennypavlova Feb 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useMemo } from 'react';
import React, { useMemo, useState } from 'react';

import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, type EuiAccordionProps } from '@elastic/eui';
import { useSummaryTimeRange } from '@kbn/observability-plugin/public';
import type { TimeRange } from '@kbn/es-query';
import type { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
Expand All @@ -24,6 +24,8 @@ import { ALERT_STATUS_ALL } from '../../../../common/alerts/constants';
import { AlertsSectionTitle } from '../../components/section_titles';
import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props';
import { CollapsibleSection } from './section/collapsible_section';
import { AlertsClosedContent } from './alerts_closed_content';
import { type AlertsCount } from '../../../../hooks/use_alerts_count';

export const AlertsSummaryContent = ({
assetName,
Expand All @@ -37,6 +39,9 @@ export const AlertsSummaryContent = ({
const { featureFlags } = usePluginConfig();
const [isAlertFlyoutVisible, { toggle: toggleAlertFlyout }] = useBoolean(false);
const { overrides } = useAssetDetailsRenderPropsContext();
const [collapsibleStatus, setCollapsibleStatus] =
useState<EuiAccordionProps['forceState']>('open');
const [activeAlertsCount, setActiveAlertsCount] = useState<number | undefined>(undefined);

const alertsEsQueryByStatus = useMemo(
() =>
Expand All @@ -48,13 +53,23 @@ export const AlertsSummaryContent = ({
[assetName, dateRange]
);

const onLoaded = (alertsCount?: AlertsCount) => {
const { activeAlertCount = 0 } = alertsCount ?? {};
const hasActiveAlerts = activeAlertCount > 0;

setCollapsibleStatus(hasActiveAlerts ? 'open' : 'closed');
setActiveAlertsCount(alertsCount?.activeAlertCount);
};

return (
<>
<CollapsibleSection
title={AlertsSectionTitle}
collapsible
data-test-subj="infraAssetDetailsAlertsCollapsible"
id="alerts"
closedSectionContent={<AlertsClosedContent activeAlertCount={activeAlertsCount} />}
initialTriggerValue={collapsibleStatus}
extraAction={
<EuiFlexGroup alignItems="center" responsive={false}>
{featureFlags.inventoryThresholdAlertRuleEnabled && (
Expand All @@ -72,9 +87,12 @@ export const AlertsSummaryContent = ({
</EuiFlexGroup>
}
>
<MemoAlertSummaryWidget alertsQuery={alertsEsQueryByStatus} dateRange={dateRange} />
<MemoAlertSummaryWidget
onLoaded={onLoaded}
alertsQuery={alertsEsQueryByStatus}
dateRange={dateRange}
/>
</CollapsibleSection>

{featureFlags.inventoryThresholdAlertRuleEnabled && (
<AlertFlyout
filter={`${findInventoryFields(assetType).name}: "${assetName}"`}
Expand All @@ -91,10 +109,11 @@ export const AlertsSummaryContent = ({
interface MemoAlertSummaryWidgetProps {
alertsQuery: AlertsEsQuery;
dateRange: TimeRange;
onLoaded: (alertsCount?: AlertsCount) => void;
}

const MemoAlertSummaryWidget = React.memo(
({ alertsQuery, dateRange }: MemoAlertSummaryWidgetProps) => {
({ alertsQuery, dateRange, onLoaded }: MemoAlertSummaryWidgetProps) => {
const { services } = useKibanaContextForPlugin();

const summaryTimeRange = useSummaryTimeRange(dateRange);
Expand All @@ -112,6 +131,7 @@ const MemoAlertSummaryWidget = React.memo(
featureIds={infraAlertFeatureIds}
filter={alertsQuery}
timeRange={summaryTimeRange}
onLoaded={onLoaded}
fullSize
hideChart
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { EuiBadge, EuiToolTip } from '@elastic/eui';

export const AlertsClosedContent = ({ activeAlertCount }: { activeAlertCount?: number }) => {
const shouldRenderAlertsClosedContent = typeof activeAlertCount === 'number';

if (!shouldRenderAlertsClosedContent) {
return null;
}
Comment on lines +12 to +16
Copy link
Contributor

@thomheymann thomheymann Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be the responsibility of the consumer to decide wether they want to render this component or not instead of having a component that doesn't render anything when no activeAlertCount prop has been passed in.

What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @thomheymann, you are right in general but the idea here is that before we set the activeAlertCount we don't want to display any message and the message should be shown once we have the activeAlertCount - so when it's a number.


if (activeAlertCount > 0) {
return (
<EuiToolTip
position="bottom"
content={i18n.translate('xpack.infra.assetDetails.tooltip.activeAlertsExplanation', {
defaultMessage: 'Active alerts',
})}
>
<EuiBadge iconType="warning" color="danger">
{activeAlertCount}
</EuiBadge>
</EuiToolTip>
);
}

return (
<span data-test-subj="infraAssetDetailsAlertsClosedContentNoAlerts">
{i18n.translate('xpack.infra.assetDetails.noActiveAlertsContentClosedSection', {
defaultMessage: 'No active alerts',
})}
</span>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
* 2.0.
*/

import React, { useEffect, useState } from 'react';

import {
EuiAccordion,
EuiFlexGroup,
EuiFlexItem,
useGeneratedHtmlId,
type EuiAccordionProps,
} from '@elastic/eui';
import React, { useState } from 'react';

export const CollapsibleSection = ({
title,
Expand All @@ -22,6 +23,7 @@ export const CollapsibleSection = ({
collapsible,
['data-test-subj']: dataTestSubj,
id,
initialTriggerValue,
}: {
title: React.FunctionComponent;
closedSectionContent?: React.ReactNode;
Expand All @@ -31,13 +33,18 @@ export const CollapsibleSection = ({
collapsible: boolean;
['data-test-subj']: string;
id: string;
initialTriggerValue?: EuiAccordionProps['forceState'];
}) => {
const [trigger, setTrigger] = useState<EuiAccordionProps['forceState']>('open');

useEffect(() => {
setTrigger(initialTriggerValue ?? 'open');
}, [initialTriggerValue]);

const Title = title;
const ButtonContent = () =>
closedSectionContent && trigger === 'closed' ? (
<EuiFlexGroup gutterSize="m">
<EuiFlexGroup gutterSize="m" alignItems="center">
<EuiFlexItem grow={false}>
<Title />
</EuiFlexItem>
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/infra/public/hooks/use_alerts_count.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ interface FetchAlertsCountParams {
signal: AbortSignal;
}

interface AlertsCount {
export interface AlertsCount {
activeAlertCount: number;
recoveredAlertCount: number;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/
import React from 'react';
import { EuiIcon, EuiLoadingSpinner, EuiNotificationBadge, EuiToolTip } from '@elastic/eui';
import { EuiIcon, EuiLoadingSpinner, EuiBadge, EuiToolTip } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useAlertsCount } from '../../../../../hooks/use_alerts_count';
import { infraAlertFeatureIds } from './config';
Expand Down Expand Up @@ -40,12 +40,12 @@ export const AlertsTabBadge = () => {
typeof alertsCount?.activeAlertCount === 'number' && alertsCount.activeAlertCount > 0;

return shouldRenderBadge ? (
<EuiNotificationBadge
<EuiBadge
color="danger"
className="eui-alignCenter"
size="m"
data-test-subj="hostsView-tabs-alerts-count"
>
{alertsCount?.activeAlertCount}
</EuiNotificationBadge>
</EuiBadge>
) : null;
};
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ export const AlertSummaryWidget = ({

useEffect(() => {
if (!isLoading && onLoaded) {
onLoaded();
onLoaded({ activeAlertCount, recoveredAlertCount });
}
}, [isLoading, onLoaded]);
}, [activeAlertCount, isLoading, onLoaded, recoveredAlertCount]);

if (isLoading)
return <AlertSummaryWidgetLoader fullSize={fullSize} isLoadingWithoutChart={hideChart} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export interface ChartProps {
onBrushEnd?: BrushEndListener;
}

interface AlertsCount {
activeAlertCount: number;
recoveredAlertCount: number;
}

export interface AlertSummaryWidgetProps {
featureIds?: ValidFeatureId[];
filter?: estypes.QueryDslQueryContainer;
Expand All @@ -38,5 +43,5 @@ export interface AlertSummaryWidgetProps {
timeRange: AlertSummaryTimeRange;
chartProps: ChartProps;
hideChart?: boolean;
onLoaded?: () => void;
onLoaded?: (alertsCount?: AlertsCount) => void;
}
45 changes: 45 additions & 0 deletions x-pack/test/functional/apps/infra/node_details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await pageObjects.assetDetails.overviewAlertsTitleExists();
});

it('should show / hide alerts section with no alerts and show / hide closed section content', async () => {
await pageObjects.assetDetails.alertsSectionCollapsibleExist();
// Collapsed by default
await pageObjects.assetDetails.alertsSectionClosedContentNoAlertsExist();
// Expand
await pageObjects.assetDetails.alertsSectionCollapsibleClick();
await pageObjects.assetDetails.alertsSectionClosedContentNoAlertsMissing();
});

it('shows the CPU Profiling prompt if UI setting for Profiling integration is enabled', async () => {
await setInfrastructureProfilingIntegrationUiSetting(true);
await pageObjects.assetDetails.cpuProfilingPromptExists();
Expand All @@ -213,6 +222,42 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await setInfrastructureProfilingIntegrationUiSetting(false);
await pageObjects.assetDetails.cpuProfilingPromptMissing();
});

describe('Alerts Section with alerts', () => {
before(async () => {
await navigateToNodeDetails('demo-stack-apache-01', 'demo-stack-apache-01');
await pageObjects.header.waitUntilLoadingHasFinished();

await pageObjects.timePicker.setAbsoluteRange(
START_HOST_ALERTS_DATE.format(DATE_PICKER_FORMAT),
END_HOST_ALERTS_DATE.format(DATE_PICKER_FORMAT)
);

await pageObjects.assetDetails.clickOverviewTab();
});

after(async () => {
await navigateToNodeDetails('Jennys-MBP.fritz.box', 'Jennys-MBP.fritz.box');
await pageObjects.header.waitUntilLoadingHasFinished();

await pageObjects.timePicker.setAbsoluteRange(
START_HOST_PROCESSES_DATE.format(DATE_PICKER_FORMAT),
END_HOST_PROCESSES_DATE.format(DATE_PICKER_FORMAT)
);
});

it('should show / hide alerts section with active alerts and show / hide closed section content', async () => {
await pageObjects.assetDetails.alertsSectionCollapsibleExist();
// Expanded by default
await pageObjects.assetDetails.alertsSectionClosedContentMissing();
// Collapse
await pageObjects.assetDetails.alertsSectionCollapsibleClick();
await pageObjects.assetDetails.alertsSectionClosedContentExist();
// Expand
await pageObjects.assetDetails.alertsSectionCollapsibleClick();
await pageObjects.assetDetails.alertsSectionClosedContentMissing();
});
});
});

describe('Metadata Tab', () => {
Expand Down
18 changes: 18 additions & 0 deletions x-pack/test/functional/page_objects/asset_details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,24 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
return await testSubjects.existOrFail('infraAssetDetailsMetricsCollapsible');
},

async alertsSectionCollapsibleClick() {
return await testSubjects.click('infraAssetDetailsAlertsCollapsible');
},

async alertsSectionClosedContentExist() {
return await testSubjects.existOrFail('infraAssetDetailsAlertsClosedContentWithAlerts');
},
async alertsSectionClosedContentMissing() {
return await testSubjects.missingOrFail('infraAssetDetailsAlertsClosedContentWithAlerts');
},

async alertsSectionClosedContentNoAlertsExist() {
return await testSubjects.existOrFail('infraAssetDetailsAlertsClosedContentNoAlerts');
},
async alertsSectionClosedContentNoAlertsMissing() {
return await testSubjects.missingOrFail('infraAssetDetailsAlertsClosedContentNoAlerts');
},

// Metadata
async clickMetadataTab() {
return testSubjects.click('infraAssetDetailsMetadataTab');
Expand Down