mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[RCA] Add from library to the investigation details page (#192217)
This commit is contained in:
parent
94aac7ad09
commit
b02e1f3987
7 changed files with 192 additions and 47 deletions
|
@ -21,7 +21,7 @@ import {
|
|||
import { METRIC_TYPE } from '@kbn/analytics';
|
||||
import { apiHasType } from '@kbn/presentation-publishing';
|
||||
import { Toast } from '@kbn/core/public';
|
||||
import { PresentationContainer } from '@kbn/presentation-containers';
|
||||
import { CanAddNewPanel } from '@kbn/presentation-containers';
|
||||
import {
|
||||
core,
|
||||
embeddableStart,
|
||||
|
@ -85,7 +85,7 @@ export const AddPanelFlyout = ({
|
|||
onAddPanel,
|
||||
modalTitleId,
|
||||
}: {
|
||||
container: PresentationContainer;
|
||||
container: CanAddNewPanel;
|
||||
onAddPanel?: (id: string) => void;
|
||||
modalTitleId?: string;
|
||||
}) => {
|
||||
|
|
|
@ -13,7 +13,7 @@ import { OverlayRef } from '@kbn/core/public';
|
|||
import { EuiLoadingSpinner, htmlIdGenerator } from '@elastic/eui';
|
||||
import { toMountPoint } from '@kbn/react-kibana-mount';
|
||||
|
||||
import { PresentationContainer } from '@kbn/presentation-containers';
|
||||
import { CanAddNewPanel } from '@kbn/presentation-containers';
|
||||
import { core } from '../kibana_services';
|
||||
|
||||
const LazyAddPanelFlyout = React.lazy(async () => {
|
||||
|
@ -28,7 +28,7 @@ export const openAddPanelFlyout = ({
|
|||
onAddPanel,
|
||||
onClose,
|
||||
}: {
|
||||
container: PresentationContainer;
|
||||
container: CanAddNewPanel;
|
||||
onAddPanel?: (id: string) => void;
|
||||
onClose?: () => void;
|
||||
}): OverlayRef => {
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
|
||||
import { IconType } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { PresentationContainer } from '@kbn/presentation-containers';
|
||||
import { CanAddNewPanel } from '@kbn/presentation-containers';
|
||||
import { FinderAttributes, SavedObjectCommon } from '@kbn/saved-objects-finder-plugin/common';
|
||||
import { SavedObjectMetaData } from '@kbn/saved-objects-finder-plugin/public';
|
||||
|
||||
type SOToEmbeddable<TSavedObjectAttributes extends FinderAttributes = FinderAttributes> = (
|
||||
container: PresentationContainer,
|
||||
container: CanAddNewPanel,
|
||||
savedObject: SavedObjectCommon<TSavedObjectAttributes>
|
||||
) => void;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { EuiLoadingSpinner, EuiFlexItem } from '@elastic/eui';
|
||||
import { css } from '@emotion/css';
|
||||
import { ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public';
|
||||
import type { GlobalWidgetParameters } from '@kbn/investigate-plugin/public';
|
||||
|
@ -15,6 +15,8 @@ import { ErrorMessage } from '../../components/error_message';
|
|||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import { Options } from '../register_items';
|
||||
|
||||
export const EMBEDDABLE_ITEM_TYPE = 'embeddable';
|
||||
|
||||
const embeddableClassName = css`
|
||||
height: 100%;
|
||||
> [data-shared-item] {
|
||||
|
@ -34,8 +36,9 @@ function ReactEmbeddable({ type, config, timeRange: { from, to }, savedObjectId
|
|||
from,
|
||||
to,
|
||||
},
|
||||
savedObjectId,
|
||||
};
|
||||
}, [config, from, to]);
|
||||
}, [config, from, to, savedObjectId]);
|
||||
|
||||
const configWithOverridesRef = useRef(configWithOverrides);
|
||||
|
||||
|
@ -48,18 +51,14 @@ function ReactEmbeddable({ type, config, timeRange: { from, to }, savedObjectId
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<ReactEmbeddableRenderer
|
||||
type={type}
|
||||
getParentApi={() => api}
|
||||
maybeId={savedObjectId}
|
||||
onAnyStateChange={(state) => {
|
||||
// console.log('onAnyStateChange', state);
|
||||
}}
|
||||
onApiAvailable={(childApi) => {
|
||||
// console.log('onApiAvailable', childApi);
|
||||
}}
|
||||
hidePanelChrome
|
||||
/>
|
||||
<div className={embeddableClassName}>
|
||||
<ReactEmbeddableRenderer
|
||||
type={type}
|
||||
getParentApi={() => api}
|
||||
maybeId={savedObjectId}
|
||||
hidePanelChrome
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -166,7 +165,7 @@ export function registerEmbeddableItem({
|
|||
services,
|
||||
}: Options) {
|
||||
investigate.registerItemDefinition<EmbeddableItemParams, {}>({
|
||||
type: 'embeddable',
|
||||
type: EMBEDDABLE_ITEM_TYPE,
|
||||
generate: async (option: {
|
||||
itemParams: EmbeddableItemParams;
|
||||
globalParams: GlobalWidgetParameters;
|
||||
|
@ -184,7 +183,18 @@ export function registerEmbeddableItem({
|
|||
timeRange: option.globalParams.timeRange,
|
||||
};
|
||||
|
||||
return <EmbeddableWidget {...parameters} />;
|
||||
return (
|
||||
<EuiFlexItem
|
||||
grow={true}
|
||||
className={css`
|
||||
> div {
|
||||
height: 196px;
|
||||
}
|
||||
`}
|
||||
>
|
||||
<EmbeddableWidget {...parameters} />
|
||||
</EuiFlexItem>
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* 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 { OverlayRef } from '@kbn/core/public';
|
||||
import { v4 } from 'uuid';
|
||||
import { openAddPanelFlyout } from '@kbn/embeddable-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { FinderAttributes } from '@kbn/saved-objects-finder-plugin/common';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { Item } from '@kbn/investigation-shared';
|
||||
import { CanAddNewPanel } from '@kbn/presentation-containers';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { EMBEDDABLE_ITEM_TYPE } from '../../../../items/embeddable_item/register_embeddable_item';
|
||||
import { useKibana } from '../../../../hooks/use_kibana';
|
||||
interface AddFromLibraryButtonProps {
|
||||
onItemAdd: (item: Item) => Promise<void>;
|
||||
}
|
||||
|
||||
type InvestigationContainer = CanAddNewPanel & {
|
||||
addNewEmbeddable: (
|
||||
type: string,
|
||||
explicitInput: { savedObjectId: string },
|
||||
attributes: FinderAttributes
|
||||
) => Promise<{ id: string }>;
|
||||
};
|
||||
|
||||
export function AddFromLibraryButton({ onItemAdd }: AddFromLibraryButtonProps) {
|
||||
const {
|
||||
dependencies: {
|
||||
start: { contentManagement },
|
||||
},
|
||||
} = useKibana();
|
||||
|
||||
const panelRef = useRef<OverlayRef>();
|
||||
|
||||
const container = useMemo<
|
||||
InvestigationContainer & {
|
||||
addNewEmbeddable: (
|
||||
type: string,
|
||||
explicitInput: { savedObjectId: string },
|
||||
attributes: FinderAttributes
|
||||
) => Promise<{ id: string }>;
|
||||
}
|
||||
>(() => {
|
||||
function addEmbeddable({
|
||||
type,
|
||||
title,
|
||||
attributes,
|
||||
savedObjectId,
|
||||
}: {
|
||||
type: string;
|
||||
title: string;
|
||||
attributes: Record<string, any>;
|
||||
savedObjectId: string;
|
||||
}) {
|
||||
const embeddableItem = {
|
||||
title,
|
||||
type: EMBEDDABLE_ITEM_TYPE,
|
||||
params: {
|
||||
savedObjectId,
|
||||
config: {},
|
||||
type,
|
||||
},
|
||||
};
|
||||
onItemAdd(embeddableItem).then(() => {
|
||||
if (panelRef.current) {
|
||||
panelRef.current.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
addNewPanel: async (panel, displaySuccessMessage) => {
|
||||
const state = panel.initialState! as {
|
||||
savedObjectId: string;
|
||||
};
|
||||
const savedObject = (await contentManagement.client.get({
|
||||
contentTypeId: panel.panelType,
|
||||
id: state.savedObjectId,
|
||||
})) as { item: { attributes: { title: string } } };
|
||||
addEmbeddable({
|
||||
type: panel.panelType,
|
||||
savedObjectId: state.savedObjectId,
|
||||
attributes: {},
|
||||
title: savedObject.item.attributes.title,
|
||||
});
|
||||
|
||||
return undefined as any;
|
||||
},
|
||||
addNewEmbeddable: async (type, explicitInput, attributes) => {
|
||||
addEmbeddable({
|
||||
type,
|
||||
title: attributes.title ?? '',
|
||||
savedObjectId: explicitInput.savedObjectId,
|
||||
attributes,
|
||||
});
|
||||
return { id: v4() };
|
||||
},
|
||||
};
|
||||
}, [contentManagement.client, onItemAdd]);
|
||||
|
||||
return (
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="investigateAppAddFromLibraryButtonImportFromLibraryButton"
|
||||
iconType="importAction"
|
||||
onClick={() => {
|
||||
panelRef.current = openAddPanelFlyout({
|
||||
container,
|
||||
});
|
||||
|
||||
panelRef.current.onClose.then(() => {
|
||||
panelRef.current = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
{i18n.translate('xpack.investigateApp.addFromLibraryButtonLabel', {
|
||||
defaultMessage: 'Import from library',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
}
|
|
@ -10,6 +10,7 @@ import { css } from '@emotion/css';
|
|||
import { TextBasedLangEditor } from '@kbn/esql/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import { AddFromLibraryButton } from '../add_from_library_button';
|
||||
import { useInvestigation } from '../../contexts/investigation_context';
|
||||
import { EsqlWidgetPreview } from './esql_widget_preview';
|
||||
|
||||
|
@ -64,7 +65,7 @@ export function AddInvestigationItem() {
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<EuiPanel color="subdued" hasShadow={false}>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
<EuiFlexGroup gutterSize="m">
|
||||
<EuiFlexItem>
|
||||
<TextBasedLangEditor
|
||||
query={query}
|
||||
|
@ -86,36 +87,44 @@ export function AddInvestigationItem() {
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
|
||||
{!isPreviewOpen ? (
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
gutterSize="l"
|
||||
className={emptyPreview}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="image" size="xxl" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<p>
|
||||
{i18n.translate(
|
||||
'xpack.investigateApp.addObservationUI.p.selectADataSourceLabel',
|
||||
{ defaultMessage: 'Select a data source to generate a preview chart' }
|
||||
)}
|
||||
</p>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EsqlWidgetPreview
|
||||
esqlQuery={submittedQuery.esql}
|
||||
timeRange={globalParams.timeRange}
|
||||
<EuiFlexItem grow={false}>
|
||||
<AddFromLibraryButton
|
||||
onItemAdd={async (item) => {
|
||||
resetState();
|
||||
await addItem(item);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{!isPreviewOpen ? (
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
gutterSize="l"
|
||||
className={emptyPreview}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiIcon type="image" size="xxl" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<p>
|
||||
{i18n.translate(
|
||||
'xpack.investigateApp.addObservationUI.p.selectADataSourceLabel',
|
||||
{ defaultMessage: 'Select a data source to generate a preview chart' }
|
||||
)}
|
||||
</p>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EsqlWidgetPreview
|
||||
esqlQuery={submittedQuery.esql}
|
||||
timeRange={globalParams.timeRange}
|
||||
onItemAdd={async (item) => {
|
||||
resetState();
|
||||
await addItem(item);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexGroup>
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
"@kbn/shared-ux-router",
|
||||
"@kbn/investigation-shared",
|
||||
"@kbn/core-security-common",
|
||||
"@kbn/saved-objects-finder-plugin",
|
||||
"@kbn/presentation-containers",
|
||||
"@kbn/lens-embeddable-utils",
|
||||
"@kbn/i18n-react",
|
||||
],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue