mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Infrastructure UI] Show metadata for a single host on the host UI (#152956)
Closes [#150893](https://github.com/elastic/kibana/issues/150893) ## Summary This PR adds a flyout with single host metadata with an option to open/close it with a click on an expand/minimize icon in the table. ⚠️ This PR doesn't include metadata filtering/actions, or processes tab inside flyout (they will be added in follow-up issues). For now, the metadata will be displayed and no actions will be available. This PR will unblock [#151010](https://github.com/elastic/kibana/issues/151010), [#150907](https://github.com/elastic/kibana/issues/150907) and [#150985](https://github.com/elastic/kibana/issues/150985) ## Testing - Open the hosts view and click on the expand icon for a single host in the table <img width="1464" alt="image" src="https://user-images.githubusercontent.com/14139027/224077010-71aece78-40d1-4a3a-90a6-8e699001b37a.png"> - The flyout should be visible with a preselected metadata tab containing - Host name as the flyout title - Metadata in a table view with field and value columns <img width="1807" alt="image" src="https://user-images.githubusercontent.com/14139027/224048634-cd49aa0f-f1a5-4442-9fd0-f16cd4cb84da.png"> - The flyout can be closed using the close icon and the minimize icon or can show a different host when another host is expanded <img width="1727" alt="image" src="https://user-images.githubusercontent.com/14139027/224084969-daa525c5-4ec4-4504-b072-4711db63fe18.png"> --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani01@gmail.com>
This commit is contained in:
parent
be71713035
commit
6591aa975e
7 changed files with 462 additions and 19 deletions
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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, { useMemo, useState } from 'react';
|
||||
import { EuiFlyout, EuiFlyoutHeader, EuiTitle, EuiFlyoutBody } from '@elastic/eui';
|
||||
import { EuiSpacer, EuiTabs, EuiTab } from '@elastic/eui';
|
||||
import { MetadataTab } from './metadata/metadata';
|
||||
import type { HostNodeRow } from '../../hooks/use_hosts_table';
|
||||
import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
|
||||
|
||||
interface Props {
|
||||
node: HostNodeRow;
|
||||
closeFlyout: () => void;
|
||||
}
|
||||
|
||||
const flyoutTabs = [MetadataTab];
|
||||
|
||||
export const Flyout = ({ node, closeFlyout }: Props) => {
|
||||
const { getDateRangeAsTimestamp } = useUnifiedSearchContext();
|
||||
|
||||
const tabs = useMemo(() => {
|
||||
const currentTimeRange = {
|
||||
...getDateRangeAsTimestamp(),
|
||||
interval: '1m',
|
||||
};
|
||||
|
||||
return flyoutTabs.map((m) => {
|
||||
const TabContent = m.content;
|
||||
return {
|
||||
...m,
|
||||
content: <TabContent node={node} currentTimeRange={currentTimeRange} />,
|
||||
};
|
||||
});
|
||||
}, [getDateRangeAsTimestamp, node]);
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState(0);
|
||||
|
||||
return (
|
||||
<EuiFlyout onClose={closeFlyout} ownFocus={false}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="xs">
|
||||
<h2>{node.name}</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiTabs style={{ marginBottom: '-25px' }} size="s">
|
||||
{tabs.map((tab, i) => (
|
||||
<EuiTab key={tab.id} isSelected={i === selectedTab} onClick={() => setSelectedTab(i)}>
|
||||
{tab.name}
|
||||
</EuiTab>
|
||||
))}
|
||||
</EuiTabs>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>{tabs[selectedTab].content}</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* 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, { useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import { EuiCallOut, EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useSourceContext } from '../../../../../../containers/metrics_source';
|
||||
import { findInventoryModel } from '../../../../../../../common/inventory_models';
|
||||
import type { InventoryItemType } from '../../../../../../../common/inventory_models/types';
|
||||
import { useMetadata } from '../../../../metric_detail/hooks/use_metadata';
|
||||
import { Table } from './table';
|
||||
import { getAllFields } from './utils';
|
||||
import type { HostNodeRow } from '../../../hooks/use_hosts_table';
|
||||
import type { MetricsTimeInput } from '../../../../metric_detail/hooks/use_metrics_time';
|
||||
|
||||
const NODE_TYPE = 'host' as InventoryItemType;
|
||||
|
||||
export interface TabProps {
|
||||
currentTimeRange: MetricsTimeInput;
|
||||
node: HostNodeRow;
|
||||
}
|
||||
|
||||
const Metadata = ({ node, currentTimeRange }: TabProps) => {
|
||||
const nodeId = node.name;
|
||||
const inventoryModel = findInventoryModel(NODE_TYPE);
|
||||
const { sourceId } = useSourceContext();
|
||||
const {
|
||||
loading: metadataLoading,
|
||||
error,
|
||||
metadata,
|
||||
} = useMetadata(nodeId, NODE_TYPE, inventoryModel.requiredMetrics, sourceId, currentTimeRange);
|
||||
|
||||
const fields = useMemo(() => getAllFields(metadata), [metadata]);
|
||||
|
||||
if (metadataLoading) {
|
||||
return <LoadingPlaceholder />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.infra.hostsViewPage.hostDetail.metadata.errorTitle', {
|
||||
defaultMessage: 'Sorry, there was an error',
|
||||
})}
|
||||
color="danger"
|
||||
iconType="error"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.hostsViewPage.hostDetail.metadata.errorMessage"
|
||||
defaultMessage="There was an error loading your data. Try to {reload} and open the host details again."
|
||||
values={{
|
||||
reload: (
|
||||
<EuiLink
|
||||
data-test-subj="infraMetadataThisLinkCanHelpLink"
|
||||
onClick={() => window.location.reload()}
|
||||
>
|
||||
{i18n.translate('xpack.infra.hostsViewPage.hostDetail.metadata.errorAction', {
|
||||
defaultMessage: 'reload the page',
|
||||
})}
|
||||
</EuiLink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</EuiCallOut>
|
||||
);
|
||||
}
|
||||
|
||||
return fields.length > 0 ? (
|
||||
<Table rows={fields} />
|
||||
) : (
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.infra.hostsViewPage.hostDetail.metadata.noMetadataFound', {
|
||||
defaultMessage: 'Sorry, there is no metadata related to this host.',
|
||||
})}
|
||||
size="m"
|
||||
iconType="iInCircle"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const LoadingPlaceholder = () => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '200px',
|
||||
padding: '16px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<EuiLoadingChart size="xl" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const MetadataTab = {
|
||||
id: 'metadata',
|
||||
name: i18n.translate('xpack.infra.nodeDetails.tabs.metadata.title', {
|
||||
defaultMessage: 'Metadata',
|
||||
}),
|
||||
content: Metadata,
|
||||
};
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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 { EuiText, EuiFlexGroup, EuiFlexItem, EuiLink, EuiBasicTable } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
interface Row {
|
||||
name: string;
|
||||
value: string | string[] | undefined;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
rows: Row[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Columns translations
|
||||
*/
|
||||
const FIELD_LABEL = i18n.translate('xpack.infra.hostsViewPage.hostDetail.metadata.field', {
|
||||
defaultMessage: 'Field',
|
||||
});
|
||||
|
||||
const VALUE_LABEL = i18n.translate('xpack.infra.hostsViewPage.hostDetail.metadata.value', {
|
||||
defaultMessage: 'Value',
|
||||
});
|
||||
|
||||
export const Table = (props: Props) => {
|
||||
const { rows } = props;
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
field: 'name',
|
||||
name: FIELD_LABEL,
|
||||
width: '35%',
|
||||
sortable: false,
|
||||
render: (name: string) => <EuiText size="s">{name}</EuiText>,
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
name: VALUE_LABEL,
|
||||
width: '65%',
|
||||
sortable: false,
|
||||
render: (_name: string, item: Row) => <ExpandableContent values={item.value} />,
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
return <EuiBasicTable tableLayout={'fixed'} responsive={false} columns={columns} items={rows} />;
|
||||
};
|
||||
|
||||
interface ExpandableContentProps {
|
||||
values: string | string[] | undefined;
|
||||
}
|
||||
const ExpandableContent = (props: ExpandableContentProps) => {
|
||||
const { values } = props;
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const expand = useCallback(() => {
|
||||
setIsExpanded(true);
|
||||
}, []);
|
||||
|
||||
const collapse = useCallback(() => {
|
||||
setIsExpanded(false);
|
||||
}, []);
|
||||
|
||||
const list = Array.isArray(values) ? values : [values];
|
||||
const [first, ...others] = list;
|
||||
const hasOthers = others.length > 0;
|
||||
const shouldShowMore = hasOthers && !isExpanded;
|
||||
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
gutterSize={'xs'}
|
||||
responsive={false}
|
||||
alignItems={'baseline'}
|
||||
wrap={true}
|
||||
direction="column"
|
||||
>
|
||||
<div>
|
||||
{first}
|
||||
{shouldShowMore && (
|
||||
<>
|
||||
{' ... '}
|
||||
<EuiLink data-test-subj="infraExpandableContentCountMoreLink" onClick={expand}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.nodeDetails.tabs.metadata.seeMore"
|
||||
defaultMessage="+{count} more"
|
||||
values={{
|
||||
count: others.length,
|
||||
}}
|
||||
/>
|
||||
</EuiLink>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{isExpanded && others.map((item) => <EuiFlexItem key={item}>{item}</EuiFlexItem>)}
|
||||
{hasOthers && isExpanded && (
|
||||
<EuiFlexItem>
|
||||
<EuiLink data-test-subj="infraExpandableContentShowLessLink" onClick={collapse}>
|
||||
{i18n.translate('xpack.infra.nodeDetails.tabs.metadata.seeLess', {
|
||||
defaultMessage: 'Show less',
|
||||
})}
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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 type { InfraMetadata } from '../../../../../../../common/http_api';
|
||||
|
||||
export const getAllFields = (metadata: InfraMetadata | null) => {
|
||||
if (!metadata?.info) return [];
|
||||
return prune([
|
||||
{
|
||||
name: 'host.architecture',
|
||||
value: metadata.info.host?.architecture,
|
||||
},
|
||||
{
|
||||
name: 'host.hostname',
|
||||
value: metadata.info.host?.name,
|
||||
},
|
||||
{
|
||||
name: 'host.id',
|
||||
value: metadata.info.host?.id,
|
||||
},
|
||||
{
|
||||
name: 'host.ip',
|
||||
value: metadata.info.host?.ip,
|
||||
},
|
||||
{
|
||||
name: 'host.mac',
|
||||
value: metadata.info.host?.mac,
|
||||
},
|
||||
{
|
||||
name: 'host.name',
|
||||
value: metadata.info.host?.name,
|
||||
},
|
||||
{
|
||||
name: 'host.os.build',
|
||||
value: metadata.info.host?.os?.build,
|
||||
},
|
||||
{
|
||||
name: 'host.os.family',
|
||||
value: metadata.info.host?.os?.family,
|
||||
},
|
||||
{
|
||||
name: 'host.os.name',
|
||||
value: metadata.info.host?.os?.name,
|
||||
},
|
||||
{
|
||||
name: 'host.os.kernel',
|
||||
value: metadata.info.host?.os?.kernel,
|
||||
},
|
||||
{
|
||||
name: 'host.os.platform',
|
||||
value: metadata.info.host?.os?.platform,
|
||||
},
|
||||
{
|
||||
name: 'host.os.version',
|
||||
value: metadata.info.host?.os?.version,
|
||||
},
|
||||
{
|
||||
name: 'cloud.account.id',
|
||||
value: metadata.info.cloud?.account?.id,
|
||||
},
|
||||
{
|
||||
name: 'cloud.account.name',
|
||||
value: metadata.info.cloud?.account?.name,
|
||||
},
|
||||
{
|
||||
name: 'cloud.availability_zone',
|
||||
value: metadata.info.cloud?.availability_zone,
|
||||
},
|
||||
{
|
||||
name: 'cloud.instance.id',
|
||||
value: metadata.info.cloud?.instance?.id,
|
||||
},
|
||||
{
|
||||
name: 'cloud.instance.name',
|
||||
value: metadata.info.cloud?.instance?.name,
|
||||
},
|
||||
{
|
||||
name: 'cloud.machine.type',
|
||||
value: metadata.info.cloud?.machine?.type,
|
||||
},
|
||||
{
|
||||
name: 'cloud.provider',
|
||||
value: metadata.info.cloud?.provider,
|
||||
},
|
||||
{
|
||||
name: 'cloud.region',
|
||||
value: metadata.info.cloud?.region,
|
||||
},
|
||||
{
|
||||
name: 'agent.id',
|
||||
value: metadata.info.agent?.id,
|
||||
},
|
||||
{
|
||||
name: 'agent.version',
|
||||
value: metadata.info.agent?.version,
|
||||
},
|
||||
{
|
||||
name: 'agent.policy',
|
||||
value: metadata.info.agent?.policy,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const prune = (fields: Array<{ name: string; value: string | string[] | undefined }>) =>
|
||||
fields.filter((f) => !!f.value);
|
|
@ -15,13 +15,18 @@ import { useHostsTable } from '../hooks/use_hosts_table';
|
|||
import { useTableProperties } from '../hooks/use_table_properties_url_state';
|
||||
import { useHostsViewContext } from '../hooks/use_hosts_view';
|
||||
import { useUnifiedSearchContext } from '../hooks/use_unified_search';
|
||||
import { Flyout } from './host_details_flyout/flyout';
|
||||
|
||||
export const HostsTable = () => {
|
||||
const { hostNodes, loading } = useHostsViewContext();
|
||||
const { onSubmit, searchCriteria } = useUnifiedSearchContext();
|
||||
const [properties, setProperties] = useTableProperties();
|
||||
|
||||
const { columns, items } = useHostsTable(hostNodes, { time: searchCriteria.dateRange });
|
||||
const { columns, items, isFlyoutOpen, closeFlyout, clickedItemUuid } = useHostsTable(hostNodes, {
|
||||
time: searchCriteria.dateRange,
|
||||
});
|
||||
|
||||
const clickedItem = items.find(({ uuid }) => uuid === clickedItemUuid);
|
||||
|
||||
const noData = items.length === 0;
|
||||
|
||||
|
@ -74,18 +79,23 @@ export const HostsTable = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<EuiInMemoryTable
|
||||
data-test-subj="hostsView-table"
|
||||
pagination={properties.pagination}
|
||||
sorting={
|
||||
typeof properties.sorting === 'boolean' ? properties.sorting : { sort: properties.sorting }
|
||||
}
|
||||
rowProps={{
|
||||
'data-test-subj': 'hostsView-tableRow',
|
||||
}}
|
||||
items={items}
|
||||
columns={columns}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
<>
|
||||
<EuiInMemoryTable
|
||||
data-test-subj="hostsView-table"
|
||||
pagination={properties.pagination}
|
||||
sorting={
|
||||
typeof properties.sorting === 'boolean'
|
||||
? properties.sorting
|
||||
: { sort: properties.sorting }
|
||||
}
|
||||
rowProps={{
|
||||
'data-test-subj': 'hostsView-tableRow',
|
||||
}}
|
||||
items={items}
|
||||
columns={columns}
|
||||
onTableChange={onTableChange}
|
||||
/>
|
||||
{isFlyoutOpen && clickedItem && <Flyout node={clickedItem} closeFlyout={closeFlyout} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,6 +9,10 @@ import { useHostsTable } from './use_hosts_table';
|
|||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { SnapshotNode } from '../../../../../common/http_api';
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
v4: () => 'uuidv4',
|
||||
}));
|
||||
|
||||
describe('useHostTable hook', () => {
|
||||
it('it should map the nodes returned from the snapshot api to a format matching eui table items', () => {
|
||||
const nodes: SnapshotNode[] = [
|
||||
|
@ -73,6 +77,7 @@ describe('useHostTable hook', () => {
|
|||
{
|
||||
name: 'host-0',
|
||||
os: '-',
|
||||
uuid: 'uuidv4',
|
||||
title: {
|
||||
cloudProvider: 'aws',
|
||||
name: 'host-0',
|
||||
|
@ -102,6 +107,7 @@ describe('useHostTable hook', () => {
|
|||
{
|
||||
name: 'host-1',
|
||||
os: 'macOS',
|
||||
uuid: 'uuidv4',
|
||||
title: {
|
||||
cloudProvider: null,
|
||||
name: 'host-1',
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { EuiBasicTableColumn, EuiText } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TimeRange } from '@kbn/es-query';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana';
|
||||
import { createInventoryMetricFormatter } from '../../inventory_view/lib/create_inventory_metric_formatter';
|
||||
|
@ -33,10 +34,9 @@ export interface HostNodeRow extends HostMetrics {
|
|||
servicesOnHost?: number | null;
|
||||
title: { name: string; cloudProvider?: CloudProvider | null };
|
||||
name: string;
|
||||
uuid: string;
|
||||
}
|
||||
|
||||
// type MappedMetrics = Record<keyof HostNodeRow, SnapshotNodeMetric>;
|
||||
|
||||
interface HostTableParams {
|
||||
time: TimeRange;
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ const formatMetric = (type: SnapshotMetricInput['type'], value: number | undefin
|
|||
|
||||
const buildItemsList = (nodes: SnapshotNode[]) => {
|
||||
return nodes.map(({ metrics, path, name }) => ({
|
||||
uuid: uuidv4(),
|
||||
name,
|
||||
os: path.at(-1)?.os ?? '-',
|
||||
title: {
|
||||
|
@ -107,6 +108,13 @@ const averageMemoryUsageLabel = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
const toggleDialogActionLabel = i18n.translate(
|
||||
'xpack.infra.hostsViewPage.table.toggleDialogWithDetails',
|
||||
{
|
||||
defaultMessage: 'Toggle dialog with details',
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Build a table columns and items starting from the snapshot nodes.
|
||||
*/
|
||||
|
@ -115,6 +123,11 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams)
|
|||
services: { telemetry },
|
||||
} = useKibanaContextForPlugin();
|
||||
|
||||
const [isFlyoutOpen, setIsFlyoutOpen] = useState(false);
|
||||
const [clickedItemUuid, setClickedItemUuid] = useState(() => uuidv4());
|
||||
|
||||
const closeFlyout = () => setIsFlyoutOpen(false);
|
||||
|
||||
const reportHostEntryClick = useCallback(
|
||||
({ name, cloudProvider }: HostNodeRow['title']) => {
|
||||
telemetry.reportHostEntryClicked({
|
||||
|
@ -129,6 +142,27 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams)
|
|||
|
||||
const columns: Array<EuiBasicTableColumn<HostNodeRow>> = useMemo(
|
||||
() => [
|
||||
{
|
||||
name: '',
|
||||
width: '40px',
|
||||
field: 'uuid',
|
||||
actions: [
|
||||
{
|
||||
name: toggleDialogActionLabel,
|
||||
description: toggleDialogActionLabel,
|
||||
icon: ({ uuid }) => (isFlyoutOpen && uuid === clickedItemUuid ? 'minimize' : 'expand'),
|
||||
type: 'icon',
|
||||
onClick: ({ uuid }) => {
|
||||
setClickedItemUuid(uuid);
|
||||
if (isFlyoutOpen && uuid === clickedItemUuid) {
|
||||
setIsFlyoutOpen(false);
|
||||
} else {
|
||||
setIsFlyoutOpen(true);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: titleLabel,
|
||||
field: 'title',
|
||||
|
@ -191,8 +225,8 @@ export const useHostsTable = (nodes: SnapshotNode[], { time }: HostTableParams)
|
|||
align: 'right',
|
||||
},
|
||||
],
|
||||
[reportHostEntryClick, time]
|
||||
[clickedItemUuid, isFlyoutOpen, reportHostEntryClick, time]
|
||||
);
|
||||
|
||||
return { columns, items };
|
||||
return { columns, items, isFlyoutOpen, closeFlyout, clickedItemUuid };
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue