Skip to content

Commit

Permalink
[Infra] Add collapsible sections (elastic#176048)
Browse files Browse the repository at this point in the history
Closes elastic#175558

## Summary

This PR makes all asset details overview sections collapsible. All
sections will be always open by default and the user should be able to
expand/collapse a section.

 
<img width="300" alt="Screenshot 2024-02-01 at 12 04 24"
src="https://github.com/elastic/kibana/assets/14139027/b91a190a-eaec-4031-8591-f11a3a04f201">
<img width="300" alt="Screenshot 2024-02-01 at 12 03 50"
src="https://github.com/elastic/kibana/assets/14139027/9d8c20fc-3a27-481c-89ad-1fd7f0e6df47">

## Testing

1. Open Host view
2. Open the hosts flyout and go to the overview tab
   - All sections should be collapsible
   - All section buttons should be clickable


https://github.com/elastic/kibana/assets/14139027/27a6df67-c560-42d6-9166-a8f45880cf8e

3. Open the asset details page and go to the overview tab
   - All sections should be collapsible
   - All section buttons should be clickable


https://github.com/elastic/kibana/assets/14139027/f7225ddd-c2a3-4a7b-b520-22acb41e9080
  • Loading branch information
jennypavlova authored and CoenWarmer committed Feb 15, 2024
1 parent 2406555 commit 21c2e14
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ export const Popover = ({
<EuiPopover
panelPaddingSize="s"
button={
<button onClick={togglePopover} data-test-subj={props['data-test-subj']}>
<button
onClick={(e) => {
e.stopPropagation();
togglePopover();
}}
data-test-subj={props['data-test-subj']}
>
<EuiIcon
type="questionInCircle"
color={iconColor ?? 'text'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import React, { useMemo } from 'react';

import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem } 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 @@ -23,6 +23,7 @@ import { useBoolean } from '../../../../hooks/use_boolean';
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';

export const AlertsSummaryContent = ({
assetName,
Expand All @@ -49,25 +50,30 @@ export const AlertsSummaryContent = ({

return (
<>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center" responsive={false}>
<EuiFlexItem>
<AlertsSectionTitle />
</EuiFlexItem>
{featureFlags.inventoryThresholdAlertRuleEnabled && (
<EuiFlexItem grow={false}>
<LinkToAlertsRule onClick={toggleAlertFlyout} />
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<LinkToAlertsPage
assetName={assetName}
queryField={`${assetType}.name`}
dateRange={dateRange}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
<MemoAlertSummaryWidget alertsQuery={alertsEsQueryByStatus} dateRange={dateRange} />
<CollapsibleSection
title={AlertsSectionTitle}
collapsible
data-test-subj="infraAssetDetailsAlertsCollapsible"
id="alerts"
extraAction={
<EuiFlexGroup alignItems="center" responsive={false}>
{featureFlags.inventoryThresholdAlertRuleEnabled && (
<EuiFlexItem grow={false}>
<LinkToAlertsRule onClick={toggleAlertFlyout} />
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<LinkToAlertsPage
assetName={assetName}
queryField={`${assetType}.name`}
dateRange={dateRange}
/>
</EuiFlexItem>
</EuiFlexGroup>
}
>
<MemoAlertSummaryWidget alertsQuery={alertsEsQueryByStatus} dateRange={dateRange} />
</CollapsibleSection>

{featureFlags.inventoryThresholdAlertRuleEnabled && (
<AlertFlyout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
EuiDescriptionList,
EuiDescriptionListDescription,
EuiLoadingSpinner,
EuiSpacer,
} from '@elastic/eui';
import type { InfraMetadata } from '../../../../../../common/http_api';
import { NOT_AVAILABLE_LABEL } from '../../../translations';
Expand All @@ -23,6 +24,7 @@ import { ExpandableContent } from '../../../components/expandable_content';
import { MetadataHeader } from './metadata_header';
import { MetadataExplanationMessage } from '../../../components/metadata_explanation';
import { MetadataSectionTitle } from '../../../components/section_titles';
import { CollapsibleSection } from '../section/collapsible_section';

interface MetadataSummaryProps {
metadata: InfraMetadata | null;
Expand Down Expand Up @@ -80,52 +82,51 @@ const MetadataSummaryListWrapper = ({

return (
<EuiFlexGroup gutterSize="s" justifyContent="spaceBetween" direction="column" wrap>
<EuiFlexGroup direction="column" gutterSize="xs">
<EuiFlexGroup justifyContent="spaceBetween" responsive={false}>
<EuiFlexItem grow={false}>
<MetadataSectionTitle />
</EuiFlexItem>
<EuiFlexItem grow={false} key="metadata-link">
<EuiButtonEmpty
data-test-subj="infraAssetDetailsMetadataShowAllButton"
onClick={onClick}
size="xs"
flush="both"
iconSide="right"
iconType="sortRight"
>
<FormattedMessage
id="xpack.infra.assetDetailsEmbeddable.metadataSummary.showAllMetadataButton"
defaultMessage="Show all"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem>
<MetadataExplanationMessage />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexGroup>
<EuiFlexGroup>
{visibleMetadata.map(
(metadataValue) =>
metadataValue && (
<EuiFlexItem key={metadataValue.field} grow={false}>
<EuiDescriptionList data-test-subj="infraMetadataSummaryItem" compressed>
<MetadataHeader metadataValue={metadataValue} />
<EuiDescriptionListDescription>
{metadataLoading && !metadataValue.value ? (
<EuiLoadingSpinner />
) : (
<ExpandableContent values={metadataValue.value ?? NOT_AVAILABLE_LABEL} />
)}
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
)
)}
</EuiFlexGroup>
<CollapsibleSection
title={MetadataSectionTitle}
collapsible
data-test-subj="infraAssetDetailsMetadataCollapsible"
id="metadata"
extraAction={
<EuiButtonEmpty
data-test-subj="infraAssetDetailsMetadataShowAllButton"
onClick={onClick}
size="xs"
flush="both"
iconSide="right"
iconType="sortRight"
key="metadata-link"
>
<FormattedMessage
id="xpack.infra.assetDetailsEmbeddable.metadataSummary.showAllMetadataButton"
defaultMessage="Show all"
/>
</EuiButtonEmpty>
}
>
<>
<MetadataExplanationMessage />
<EuiSpacer size="s" />
<EuiFlexGroup>
{visibleMetadata
.filter((metadataValue) => metadataValue)
.map((metadataValue) => (
<EuiFlexItem key={metadataValue.field} grow={false}>
<EuiDescriptionList data-test-subj="infraMetadataSummaryItem" compressed>
<MetadataHeader metadataValue={metadataValue} />
<EuiDescriptionListDescription>
{metadataLoading && !metadataValue.value ? (
<EuiLoadingSpinner />
) : (
<ExpandableContent values={metadataValue.value ?? NOT_AVAILABLE_LABEL} />
)}
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
))}
</EuiFlexGroup>
</>
</CollapsibleSection>
</EuiFlexGroup>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
import React, { useMemo } from 'react';

import { EuiFlexItem } from '@elastic/eui';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { TimeRange } from '@kbn/es-query';
import { EuiFlexGroup } from '@elastic/eui';
Expand All @@ -18,6 +17,7 @@ import {
} from '../../../components/section_titles';
import { useMetadataStateContext } from '../../../hooks/use_metadata_state';
import { MetricsGrid } from './metrics_grid';
import { CollapsibleSection } from '../section/collapsible_section';

interface Props {
assetName: string;
Expand Down Expand Up @@ -49,7 +49,7 @@ export const MetricsSection = ({ assetName, metricsDataView, logsDataView, dateR

return (
<EuiFlexGroup direction="column" gutterSize="s">
<Section title={MetricsSectionTitle}>
<Section title={MetricsSectionTitle} collapsible>
<MetricsGrid
assetName={assetName}
dateRange={dateRange}
Expand Down Expand Up @@ -92,7 +92,7 @@ export const MetricsSectionCompact = ({
);

return (
<Section title={MetricsSectionTitle}>
<Section title={MetricsSectionTitle} collapsible>
<MetricsGrid
assetName={assetName}
dateRange={dateRange}
Expand All @@ -107,13 +107,14 @@ export const MetricsSectionCompact = ({
const Section = ({
title,
dependsOn = [],
collapsible = false,
children,
}: {
title: React.FunctionComponent;
dependsOn?: string[];
collapsible?: boolean;
children: React.ReactNode;
}) => {
const Title = title;
const { metadata } = useMetadataStateContext();

const shouldRender = useMemo(
Expand All @@ -124,11 +125,13 @@ const Section = ({
);

return shouldRender ? (
<EuiFlexGroup gutterSize="m" direction="column">
<EuiFlexItem grow={false}>
<Title />
</EuiFlexItem>
<EuiFlexItem grow={false}>{children}</EuiFlexItem>
</EuiFlexGroup>
<CollapsibleSection
title={title}
collapsible={collapsible}
data-test-subj={`infraAssetDetailsMetrics${collapsible ? 'Collapsible' : 'Section'}`}
id="metrics"
>
{children}
</CollapsibleSection>
) : null;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* 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 {
EuiAccordion,
EuiFlexGroup,
EuiFlexItem,
useGeneratedHtmlId,
type EuiAccordionProps,
} from '@elastic/eui';
import React, { useState } from 'react';

export const CollapsibleSection = ({
title,
closedSectionContent,
extraAction,
children,
collapsible,
['data-test-subj']: dataTestSubj,
id,
}: {
title: React.FunctionComponent;
closedSectionContent?: React.ReactNode;
extraAction?: React.ReactNode;
dependsOn?: string[];
children: React.ReactNode;
collapsible: boolean;
['data-test-subj']: string;
id: string;
}) => {
const [trigger, setTrigger] = useState<EuiAccordionProps['forceState']>('open');

const Title = title;
const ButtonContent = () =>
closedSectionContent && trigger === 'closed' ? (
<EuiFlexGroup gutterSize="m">
<EuiFlexItem grow={false}>
<Title />
</EuiFlexItem>
<EuiFlexItem grow={false}>{closedSectionContent}</EuiFlexItem>
</EuiFlexGroup>
) : (
<Title />
);
const collapsibleSectionAccordionId = useGeneratedHtmlId({
prefix: id,
});

const onToggle = (isOpen: boolean) => {
const newState = isOpen ? 'open' : 'closed';
setTrigger(newState);
};

return collapsible ? (
<EuiAccordion
id={collapsibleSectionAccordionId}
data-section-id={id}
buttonElement="div"
element="fieldset"
buttonContent={<ButtonContent />}
buttonProps={{ 'data-test-subj': dataTestSubj }}
paddingSize="s"
initialIsOpen={true}
extraAction={extraAction ?? undefined}
forceState={trigger}
onToggle={onToggle}
data-section-state={trigger}
data-test-subj="infraAssetDetailsCollapseExpandSection"
>
{children}
</EuiAccordion>
) : (
<EuiFlexGroup gutterSize="m" direction="column">
<EuiFlexItem grow={false}>
<Title />
</EuiFlexItem>
<EuiFlexItem grow={false}>{children}</EuiFlexItem>
</EuiFlexGroup>
);
};
6 changes: 6 additions & 0 deletions x-pack/test/functional/apps/infra/hosts_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
expect(hosts.length).to.equal(9);
});

it('should show all section as collapsable', async () => {
await pageObjects.assetDetails.metadataSectionCollapsibleExist();
await pageObjects.assetDetails.alertsSectionCollapsibleExist();
await pageObjects.assetDetails.metricsSectionCollapsibleExist();
});

it('should show alerts', async () => {
await pageObjects.header.waitUntilLoadingHasFinished();
await pageObjects.assetDetails.overviewAlertsTitleExists();
Expand Down
6 changes: 6 additions & 0 deletions x-pack/test/functional/apps/infra/node_details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
expect(hosts.length).to.equal(12);
});

it('should show all section as collapsable', async () => {
await pageObjects.assetDetails.metadataSectionCollapsibleExist();
await pageObjects.assetDetails.alertsSectionCollapsibleExist();
await pageObjects.assetDetails.metricsSectionCollapsibleExist();
});

it('should show alerts', async () => {
await pageObjects.header.waitUntilLoadingHasFinished();
await pageObjects.assetDetails.overviewAlertsTitleExists();
Expand Down
11 changes: 11 additions & 0 deletions x-pack/test/functional/page_objects/asset_details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ export function AssetDetailsProvider({ getService }: FtrProviderContext) {
return await testSubjects.missingOrFail('infraAssetDetailsProfilingTab');
},

// Collapsable sections
async metadataSectionCollapsibleExist() {
return await testSubjects.existOrFail('infraAssetDetailsMetadataCollapsible');
},
async alertsSectionCollapsibleExist() {
return await testSubjects.existOrFail('infraAssetDetailsAlertsCollapsible');
},
async metricsSectionCollapsibleExist() {
return await testSubjects.existOrFail('infraAssetDetailsMetricsCollapsible');
},

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

0 comments on commit 21c2e14

Please sign in to comment.