mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Infra] Add collapsible sections (#176048)
Closes #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="b91a190a
-eaec-4031-8591-f11a3a04f201"> <img width="300" alt="Screenshot 2024-02-01 at 12 03 50" src="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 clickable27a6df67
-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 clickablef7225ddd
-c2a3-4a7b-b520-22acb41e9080
This commit is contained in:
parent
5104183911
commit
52217569a6
8 changed files with 200 additions and 77 deletions
|
@ -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'}
|
||||
|
|
|
@ -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';
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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}
|
||||
|
@ -92,7 +92,7 @@ export const MetricsSectionCompact = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<Section title={MetricsSectionTitle}>
|
||||
<Section title={MetricsSectionTitle} collapsible>
|
||||
<MetricsGrid
|
||||
assetName={assetName}
|
||||
dateRange={dateRange}
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue