diff --git a/src/plugins/discover/public/application/main/discover_main_app.tsx b/src/plugins/discover/public/application/main/discover_main_app.tsx index 47eac24fb48b5..3d6b55b7aa7d6 100644 --- a/src/plugins/discover/public/application/main/discover_main_app.tsx +++ b/src/plugins/discover/public/application/main/discover_main_app.tsx @@ -8,6 +8,7 @@ import React, { useCallback, useEffect } from 'react'; import { RootDragDropProvider } from '@kbn/dom-drag-drop'; import { useHistory } from 'react-router-dom'; +import { CoreStart } from '@kbn/core/public'; import { useUrlTracking } from './hooks/use_url_tracking'; import { useSearchSession } from './hooks/use_search_session'; import { DiscoverStateContainer } from './services/discover_state'; @@ -19,6 +20,7 @@ import { useSavedSearchAliasMatchRedirect } from '../../hooks/saved_search_alias import { useSavedSearchInitial } from './services/discover_state_provider'; import { useAdHocDataViews } from './hooks/use_adhoc_data_views'; import { useTextBasedQueryLanguage } from './hooks/use_text_based_query_language'; +import { DiscoverServices } from '../../build_services'; const DiscoverLayoutMemoized = React.memo(DiscoverLayout); @@ -27,13 +29,18 @@ export interface DiscoverMainProps { * Central state container */ stateContainer: DiscoverStateContainer; + + /** + * Services passed in + */ + providedServices?: Partial & DiscoverServices; } export function DiscoverMainApp(props: DiscoverMainProps) { - const { stateContainer } = props; + const { providedServices, stateContainer } = props; const savedSearch = useSavedSearchInitial(); const services = useDiscoverServices(); - const { chrome, docLinks, data, spaces, history } = services; + const { chrome, docLinks, data, spaces, history } = services ?? providedServices; const usedHistory = useHistory(); const navigateTo = useCallback( (path: string) => { @@ -47,21 +54,21 @@ export function DiscoverMainApp(props: DiscoverMainProps) { /** * Search session logic */ - useSearchSession({ services, stateContainer }); + useSearchSession({ services: services ?? providedServices, stateContainer }); /** * Adhoc data views functionality */ const { persistDataView } = useAdHocDataViews({ stateContainer, - services, + services: services ?? providedServices, }); /** * State changes (data view, columns), when a text base query result is returned */ useTextBasedQueryLanguage({ - dataViews: services.dataViews, + dataViews: services.dataViews ?? providedServices?.dataViews, stateContainer, }); /** diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index 3f0da4a4f4a7f..cb4de6e171c9b 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -16,6 +16,7 @@ import { } from '@kbn/shared-ux-page-analytics-no-data'; import { getSavedSearchFullPathUrl } from '@kbn/saved-search-plugin/public'; import useObservable from 'react-use/lib/useObservable'; +import { CoreStart } from '@kbn/core/public'; import { useUrl } from './hooks/use_url'; import { useSingleton } from './hooks/use_singleton'; import { MainHistoryLocationState } from '../../../common/locator'; @@ -28,6 +29,7 @@ import { useDiscoverServices } from '../../hooks/use_discover_services'; import { getScopedHistory, getUrlTracker } from '../../kibana_services'; import { useAlertResultsToast } from './hooks/use_alert_results_toast'; import { DiscoverMainProvider } from './services/discover_state_provider'; +import { DiscoverServices } from '../../build_services'; const DiscoverMainAppMemoized = memo(DiscoverMainApp); @@ -35,14 +37,15 @@ interface DiscoverLandingParams { id: string; } -interface Props { +export interface Props { isDev: boolean; + providedServices?: Partial & DiscoverServices; } export function DiscoverMainRoute(props: Props) { const history = useHistory(); const services = useDiscoverServices(); - const { isDev } = props; + const { isDev, providedServices } = props; const { core, chrome, @@ -55,7 +58,7 @@ export function DiscoverMainRoute(props: Props) { const stateContainer = useSingleton(() => getDiscoverStateContainer({ history, - services, + services: services ?? providedServices, }) ); const [error, setError] = useState(); @@ -255,3 +258,5 @@ export function DiscoverMainRoute(props: Props) { ); } + +export default DiscoverMainRoute; diff --git a/src/plugins/discover/public/application/services_provider.tsx b/src/plugins/discover/public/application/services_provider.tsx new file mode 100644 index 0000000000000..268ccd96f8d06 --- /dev/null +++ b/src/plugins/discover/public/application/services_provider.tsx @@ -0,0 +1,26 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { CellActionsProvider } from '@kbn/cell-actions'; +import { DiscoverServices } from '../build_services'; + +export const ServicesContextProvider: React.FC<{ services: DiscoverServices }> = ({ + services, + children, +}) => { + const { getTriggerCompatibleActions } = services.uiActions; + return ( + + + {children} + + + ); +}; diff --git a/src/plugins/discover/public/exports/discover_app.tsx b/src/plugins/discover/public/exports/discover_app.tsx new file mode 100644 index 0000000000000..f70880568ab9d --- /dev/null +++ b/src/plugins/discover/public/exports/discover_app.tsx @@ -0,0 +1,38 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { Suspense, useCallback } from 'react'; +import { ServicesContextProvider } from '../application/services_provider'; +import { DiscoverServices } from '../build_services'; +import type { Props as DiscoverMainRouteProps } from '../application/main/discover_main_route'; +import { + setHeaderActionMenuMounter, + setScopedHistory, + syncHistoryLocations, +} from '../kibana_services'; + +export type ExportedDiscoverMainRoute = Omit; + +const DiscoverMainRoute = React.lazy(() => import('../application/main/discover_main_route')); +export const useDiscoverMainRoute = (services: DiscoverServices) => { + return useCallback( + (history) => (props: ExportedDiscoverMainRoute) => { + setScopedHistory(history); + setHeaderActionMenuMounter(() => {}); + syncHistoryLocations(); + return ( + + + + + + ); + }, + [services] + ); +}; diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index 412a20d154948..068e66803c764 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -73,6 +73,7 @@ import { import { DiscoverAppLocator, DiscoverAppLocatorDefinition } from '../common'; import { SEARCH_EMBEDDABLE_CELL_ACTIONS_TRIGGER } from './embeddable/constants'; import { useDiscoverGrid } from './exports/discover_grid'; +import { useDiscoverMainRoute } from './exports/discover_app'; const DocViewerLegacyTable = React.lazy( () => import('./services/doc_views/components/doc_viewer_table/legacy') @@ -158,6 +159,7 @@ export interface DiscoverStart { */ readonly locator: undefined | DiscoverAppLocator; useDiscoverGrid: () => ReturnType; + useDiscoverMainRoute: () => ReturnType; } /** @@ -408,6 +410,9 @@ export class DiscoverPlugin useDiscoverGrid: () => { return useDiscoverGrid(services); }, + useDiscoverMainRoute: () => { + return useDiscoverMainRoute(services); + }, }; } diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index ba4a8a13501fa..46a88e4b3b989 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -444,6 +444,7 @@ export enum TimelineTabs { pinned = 'pinned', eql = 'eql', session = 'session', + discover = 'discover', } /** diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx new file mode 100644 index 0000000000000..3ab50862f79eb --- /dev/null +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/discover_tab_content/index.tsx @@ -0,0 +1,31 @@ +/* + * 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 { css } from '@emotion/react'; +import { useHistory } from 'react-router-dom'; +import { useKibana } from '../../../../common/lib/kibana'; + +const DiscoverTabContent = () => { + const history = useHistory(); + const { discover } = useKibana().services; + const { useDiscoverMainRoute } = discover; + const getDiscoverLayout = useDiscoverMainRoute(); + const DiscoverLayout = getDiscoverLayout(history); + return ( +
+ +
+ ); +}; + +// eslint-disable-next-line import/no-default-export +export default DiscoverTabContent; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index 661e38aaa3f40..53002343db99a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -52,6 +52,7 @@ const GraphTabContent = lazy(() => import('../graph_tab_content')); const NotesTabContent = lazy(() => import('../notes_tab_content')); const PinnedTabContent = lazy(() => import('../pinned_tab_content')); const SessionTabContent = lazy(() => import('../session_tab_content')); +const DiscoverTabContent = lazy(() => import('../discover_tab_content')); interface BasicTimelineTab { renderCellValue: (props: CellValueElementProps) => React.ReactNode; @@ -130,6 +131,13 @@ const PinnedTab: React.FC<{ )); PinnedTab.displayName = 'PinnedTab'; +const DiscoverTab: React.FC = memo(() => ( + }> + + +)); +DiscoverTab.displayName = 'DiscoverTab'; + type ActiveTimelineTabProps = BasicTimelineTab & { activeTimelineTab: TimelineTabs }; const ActiveTimelineTab = memo( @@ -143,6 +151,8 @@ const ActiveTimelineTab = memo( return ; case TimelineTabs.session: return ; + case TimelineTabs.discover: + return ; default: return null; } @@ -183,6 +193,12 @@ const ActiveTimelineTab = memo( timelineId={timelineId} /> + + + {timelineType === TimelineType.default && ( = ({ setActiveTab(TimelineTabs.session); }, [setActiveTab]); + const setDiscoverAsActiveTab = useCallback(() => { + setActiveTab(TimelineTabs.discover); + }, [setActiveTab]); + useEffect(() => { if (!graphEventId && activeTab === TimelineTabs.graph) { setQueryAsActiveTab(); @@ -389,6 +409,15 @@ const TabsContentComponent: React.FC = ({ )} + + {i18n.DISCOVER_TAB} + )} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/translations.ts index e3a53675389b7..b77f0f938c26f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/translations.ts @@ -45,3 +45,10 @@ export const SESSION_TAB = i18n.translate( defaultMessage: 'Session View', } ); + +export const DISCOVER_TAB = i18n.translate( + 'xpack.securitySolution.timeline.tabs.discoverTabTimelineTitle', + { + defaultMessage: 'Discover', + } +); \ No newline at end of file