mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Logs Explorer] enabling redirect link to discover ESQL (#168002)
Closes https://github.com/elastic/kibana/issues/167023. ### Changes - `EsqlSelector` was created. This component is in charge of rendering the `Try ESQL` button. #### Before <img width="1905" alt="image" src="9ffc304f
-b68c-4871-8524-1239123f5826"> #### After <img width="1900" alt="image" src="b95e8739
-b93b-47f7-884e-519b73b2ccda"> - It works as a link, so users are able to open it in a new tab if desired <img width="1899" alt="image" src="d6390ba2
-5010-4ed5-99cb-4c3f6e099dfe"> - It navigates to discover in `ES|QL` mode1f083c2d
-a06f-4f0b-9cbb-5958edcb9cce ##### For serverless <img width="1896" alt="image" src="48712921
-85f7-4848-9e71-426bded04913"> - When `discover:enableESQL` is disabled <img width="1901" alt="image" src="4267e03b
-7b46-4477-813b-65a8a04e6329"> ### Missing UI tests will come as part of a new PR. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
a867962ddb
commit
eaf77786e9
9 changed files with 193 additions and 16 deletions
|
@ -84,6 +84,14 @@ export const noDataRetryLabel = i18n.translate('xpack.logExplorer.datasetSelecto
|
|||
defaultMessage: 'Retry',
|
||||
});
|
||||
|
||||
export const tryEsql = i18n.translate('xpack.logExplorer.datasetSelector.TryEsql', {
|
||||
defaultMessage: 'Try ES|QL',
|
||||
});
|
||||
|
||||
export const technicalPreview = i18n.translate('xpack.logExplorer.TechPreview', {
|
||||
defaultMessage: 'Technical preview',
|
||||
});
|
||||
|
||||
export const sortOptions = [
|
||||
{
|
||||
id: 'asc',
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { EuiContextMenu, EuiHorizontalRule, EuiTab, EuiTabs } from '@elastic/eui';
|
||||
import styled from '@emotion/styled';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useIntersectionRef } from '../../hooks/use_intersection_ref';
|
||||
import { getDataViewTestSubj } from '../../utils/get_data_view_test_subj';
|
||||
import {
|
||||
dataViewsLabel,
|
||||
DATA_VIEWS_PANEL_ID,
|
||||
|
@ -23,6 +24,8 @@ import {
|
|||
} from './constants';
|
||||
import { useDatasetSelector } from './state_machine/use_dataset_selector';
|
||||
import { DatasetsPopover } from './sub_components/datasets_popover';
|
||||
import { DataViewsPanelTitle } from './sub_components/data_views_panel_title';
|
||||
import { EsqlSelector } from './sub_components/esql_selector';
|
||||
import { SearchControls } from './sub_components/search_controls';
|
||||
import { SelectorActions } from './sub_components/selector_actions';
|
||||
import { DatasetSelectorProps } from './types';
|
||||
|
@ -32,8 +35,6 @@ import {
|
|||
createIntegrationStatusItem,
|
||||
createUncategorizedStatusItem,
|
||||
} from './utils';
|
||||
import { getDataViewTestSubj } from '../../utils/get_data_view_test_subj';
|
||||
import { DataViewsPanelTitle } from './sub_components/data_views_panel_title';
|
||||
|
||||
export function DatasetSelector({
|
||||
datasets,
|
||||
|
@ -41,8 +42,10 @@ export function DatasetSelector({
|
|||
datasetsError,
|
||||
dataViews,
|
||||
dataViewsError,
|
||||
discoverEsqlUrlProps,
|
||||
integrations,
|
||||
integrationsError,
|
||||
isEsqlEnabled,
|
||||
isLoadingDataViews,
|
||||
isLoadingIntegrations,
|
||||
isLoadingUncategorized,
|
||||
|
@ -278,6 +281,7 @@ export function DatasetSelector({
|
|||
data-test-subj="dataViewsContextMenu"
|
||||
size="s"
|
||||
/>
|
||||
{isEsqlEnabled && <EsqlSelector {...discoverEsqlUrlProps} />}
|
||||
</DatasetsPopover>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiBadge, EuiButton, EuiHorizontalRule } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import { getRouterLinkProps } from '../../../utils/get_router_link_props';
|
||||
import { DiscoverEsqlUrlProps } from '../../../hooks/use_esql';
|
||||
import { technicalPreview, tryEsql } from '../constants';
|
||||
|
||||
export const EsqlSelector = (props: DiscoverEsqlUrlProps) => {
|
||||
const linkProps = getRouterLinkProps(props);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiHorizontalRule margin="none" />
|
||||
<EuiButton {...linkProps} color="success" size="s" fullWidth data-test-subj="esqlLink">
|
||||
{tryEsql}
|
||||
<EuiBadge color="hollow">{technicalPreview}</EuiBadge>
|
||||
</EuiButton>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -27,6 +27,7 @@ import {
|
|||
UNCATEGORIZED_TAB_ID,
|
||||
} from './constants';
|
||||
import { LoadDataViews, ReloadDataViews, SearchDataViews } from '../../hooks/use_data_views';
|
||||
import { DiscoverEsqlUrlProps } from '../../hooks/use_esql';
|
||||
|
||||
export interface DatasetSelectorProps {
|
||||
/* The generic data stream list */
|
||||
|
@ -39,6 +40,8 @@ export interface DatasetSelectorProps {
|
|||
dataViews: DataViewListItem[] | null;
|
||||
/* Any error occurred to show when the user preview the data views */
|
||||
dataViewsError: Error | null;
|
||||
/* url props to navigate to discover ES|QL */
|
||||
discoverEsqlUrlProps: DiscoverEsqlUrlProps;
|
||||
/* The integrations list, each integration includes its data streams */
|
||||
integrations: Integration[] | null;
|
||||
/* Any error occurred to show when the user preview the integrations */
|
||||
|
@ -48,6 +51,8 @@ export interface DatasetSelectorProps {
|
|||
isLoadingIntegrations: boolean;
|
||||
isLoadingUncategorized: boolean;
|
||||
isSearchingIntegrations: boolean;
|
||||
/* Flag for determining whether ESQL is enabled or not */
|
||||
isEsqlEnabled: boolean;
|
||||
/* Triggered when retrying to load the data views */
|
||||
onDataViewsReload: ReloadDataViews;
|
||||
/* Triggered when selecting a data view */
|
||||
|
|
|
@ -5,16 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import React from 'react';
|
||||
import { DatasetSelector } from '../components/dataset_selector';
|
||||
import { DatasetsProvider, useDatasetsContext } from '../hooks/use_datasets';
|
||||
import { useDatasetSelection } from '../hooks/use_dataset_selection';
|
||||
import { DataViewsProvider, useDataViewsContext } from '../hooks/use_data_views';
|
||||
import { useEsql } from '../hooks/use_esql';
|
||||
import { IntegrationsProvider, useIntegrationsContext } from '../hooks/use_integrations';
|
||||
import { IDatasetsClient } from '../services/datasets';
|
||||
import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile';
|
||||
import { useDatasetSelection } from '../hooks/use_dataset_selection';
|
||||
import { DataViewsProvider, useDataViewsContext } from '../hooks/use_data_views';
|
||||
|
||||
interface CustomDatasetSelectorProps {
|
||||
logExplorerProfileStateService: LogExplorerProfileStateService;
|
||||
|
@ -59,6 +60,8 @@ export const CustomDatasetSelector = withProviders(({ logExplorerProfileStateSer
|
|||
sortDataViews,
|
||||
} = useDataViewsContext();
|
||||
|
||||
const { isEsqlEnabled, discoverEsqlUrlProps } = useEsql({ datasetSelection });
|
||||
|
||||
return (
|
||||
<DatasetSelector
|
||||
datasets={datasets}
|
||||
|
@ -66,15 +69,17 @@ export const CustomDatasetSelector = withProviders(({ logExplorerProfileStateSer
|
|||
datasetsError={datasetsError}
|
||||
dataViews={dataViews}
|
||||
dataViewsError={dataViewsError}
|
||||
discoverEsqlUrlProps={discoverEsqlUrlProps}
|
||||
integrations={integrations}
|
||||
integrationsError={integrationsError}
|
||||
isEsqlEnabled={isEsqlEnabled}
|
||||
isLoadingDataViews={isLoadingDataViews}
|
||||
isLoadingIntegrations={isLoadingIntegrations}
|
||||
isLoadingUncategorized={isLoadingUncategorized}
|
||||
isSearchingIntegrations={isSearchingIntegrations}
|
||||
onDataViewSelection={selectDataView}
|
||||
onDataViewsReload={reloadDataViews}
|
||||
onDataViewsSearch={searchDataViews}
|
||||
onDataViewSelection={selectDataView}
|
||||
onDataViewsSort={sortDataViews}
|
||||
onDataViewsTabClick={loadDataViews}
|
||||
onIntegrationsLoadMore={loadMore}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { dynamic } from '../utils/dynamic';
|
|||
import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile';
|
||||
import { LogExplorerStateContainer } from '../components/log_explorer';
|
||||
import { LogExplorerStartDeps } from '../types';
|
||||
import { useKibanaContextForPluginProvider } from '../utils/use_kibana';
|
||||
|
||||
const LazyCustomDatasetSelector = dynamic(() => import('./custom_dataset_selector'));
|
||||
const LazyCustomDatasetFilters = dynamic(() => import('./custom_dataset_filters'));
|
||||
|
@ -69,14 +70,20 @@ export const createLogExplorerProfileCustomizations =
|
|||
*/
|
||||
customizations.set({
|
||||
id: 'search_bar',
|
||||
CustomDataViewPicker: () => (
|
||||
<LazyCustomDatasetSelector
|
||||
datasetsClient={datasetsClient}
|
||||
dataViews={dataViews}
|
||||
discover={discover}
|
||||
logExplorerProfileStateService={logExplorerProfileStateService}
|
||||
/>
|
||||
),
|
||||
CustomDataViewPicker: () => {
|
||||
const KibanaContextProviderForPlugin = useKibanaContextForPluginProvider(core, plugins);
|
||||
|
||||
return (
|
||||
<KibanaContextProviderForPlugin>
|
||||
<LazyCustomDatasetSelector
|
||||
datasetsClient={datasetsClient}
|
||||
dataViews={dataViews}
|
||||
discover={discover}
|
||||
logExplorerProfileStateService={logExplorerProfileStateService}
|
||||
/>
|
||||
</KibanaContextProviderForPlugin>
|
||||
);
|
||||
},
|
||||
PrependFilterBar: () => (
|
||||
<LazyCustomDatasetFilters
|
||||
logExplorerProfileStateService={logExplorerProfileStateService}
|
||||
|
|
52
x-pack/plugins/log_explorer/public/hooks/use_esql.tsx
Normal file
52
x-pack/plugins/log_explorer/public/hooks/use_esql.tsx
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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 { DatasetSelection } from '../../common/dataset_selection';
|
||||
import { useKibanaContextForPlugin } from '../utils/use_kibana';
|
||||
|
||||
export interface DiscoverEsqlUrlProps {
|
||||
href?: string;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
export interface UseEsqlResult {
|
||||
isEsqlEnabled: boolean;
|
||||
discoverEsqlUrlProps: DiscoverEsqlUrlProps;
|
||||
}
|
||||
|
||||
interface EsqlContextDeps {
|
||||
datasetSelection: DatasetSelection;
|
||||
}
|
||||
|
||||
export const useEsql = ({ datasetSelection }: EsqlContextDeps): UseEsqlResult => {
|
||||
const {
|
||||
services: { uiSettings, discover },
|
||||
} = useKibanaContextForPlugin();
|
||||
|
||||
const isEsqlEnabled = uiSettings?.get('discover:enableESQL');
|
||||
|
||||
const discoverLinkParams = {
|
||||
query: {
|
||||
esql: `from ${datasetSelection.selection.dataset.name} | limit 10`,
|
||||
},
|
||||
};
|
||||
|
||||
const href = discover.locator?.useUrl(discoverLinkParams);
|
||||
|
||||
const onClick = () => {
|
||||
discover.locator?.navigate(discoverLinkParams);
|
||||
};
|
||||
|
||||
return {
|
||||
// Data
|
||||
isEsqlEnabled,
|
||||
discoverEsqlUrlProps: {
|
||||
href,
|
||||
onClick,
|
||||
},
|
||||
};
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
interface GetRouterLinkPropsDeps {
|
||||
href?: string;
|
||||
onClick(): void;
|
||||
}
|
||||
|
||||
const isModifiedEvent = (event: React.MouseEvent<HTMLAnchorElement>) =>
|
||||
!!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
|
||||
|
||||
const isLeftClickEvent = (event: React.MouseEvent<HTMLAnchorElement>) => event.button === 0;
|
||||
|
||||
export const getRouterLinkProps = ({ href, onClick }: GetRouterLinkPropsDeps) => {
|
||||
const guardedClickHandler = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isModifiedEvent(event) || !isLeftClickEvent(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent regular link behavior, which causes a browser refresh.
|
||||
event.preventDefault();
|
||||
|
||||
onClick();
|
||||
};
|
||||
|
||||
return { href, onClick: guardedClickHandler };
|
||||
};
|
35
x-pack/plugins/log_explorer/public/utils/use_kibana.tsx
Normal file
35
x-pack/plugins/log_explorer/public/utils/use_kibana.tsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 { CoreStart } from '@kbn/core/public';
|
||||
import {
|
||||
createKibanaReactContext,
|
||||
KibanaReactContextValue,
|
||||
useKibana,
|
||||
} from '@kbn/kibana-react-plugin/public';
|
||||
import { useMemo } from 'react';
|
||||
import { LogExplorerStartDeps } from '../types';
|
||||
|
||||
export type PluginKibanaContextValue = CoreStart & LogExplorerStartDeps;
|
||||
|
||||
export const createKibanaContextForPlugin = (core: CoreStart, plugins: LogExplorerStartDeps) =>
|
||||
createKibanaReactContext<PluginKibanaContextValue>({
|
||||
...core,
|
||||
...plugins,
|
||||
});
|
||||
|
||||
export const useKibanaContextForPlugin =
|
||||
useKibana as () => KibanaReactContextValue<PluginKibanaContextValue>;
|
||||
|
||||
export const useKibanaContextForPluginProvider = (
|
||||
core: CoreStart,
|
||||
plugins: LogExplorerStartDeps
|
||||
) => {
|
||||
const { Provider } = useMemo(() => createKibanaContextForPlugin(core, plugins), [core, plugins]);
|
||||
|
||||
return Provider;
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue