Add data CTA to datasource selector (#177958)

## Summary

<img width="559" alt="Screenshot 2024-03-04 at 18 14 40"
src="78c92804-cbb0-4167-bb1e-58596fa81440">

Closes https://github.com/elastic/kibana/issues/176675

This PR adds the "Add data" CTA to the data selector.

## Changes

* Make data source selector wider per designs
* Add button using locator for the URL
* Fixes story for the data source selector (it was broken before - this
is a boy-scout-rule change)

## Open questions

* Should this always be shown or only as long as the "Integrations" tab
is active? In the designs there is a "New data view" button instead when
data views are selected, but the issue doesn't mention it
* This is very similar to the OnboardingLink component in the
observability_logs_explorer plugin. However, the datasource selector is
defined upstream in the logs_explorer plugin. One alternative would be
to pass the button down in a generic manner, but I'm not sure whether
it's worth the extra complexity, especially considering where the logs
explorer is going. The question is whether this redundancy is OK for now
or whether it's worth to resolve it. Another option would be to move
this button into the logs_explorer plugin and re-use it from the
observability_logs_explorer

2696d7fcd3/x-pack/plugins/observability_solution/observability_logs_explorer/public/components/onboarding_link.tsx (L29)

Personally I think moving this button into the logs_explorer plugin and
re-use it from the observability_logs_explorer is the best trade-off
right now, but interested in your thoughts.
This commit is contained in:
Joe Reuter 2024-03-07 10:01:27 +01:00 committed by GitHub
parent 7390350791
commit 4cd65b4a3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 101 additions and 21 deletions

View file

@ -15,13 +15,17 @@ export const UNCATEGORIZED_TAB_ID = 'data-source-selector-uncategorized-tab';
export const DATA_VIEWS_PANEL_ID = 'data-source-selector-data-views-panel';
export const DATA_VIEWS_TAB_ID = 'data-source-selector-data-views-tab';
export const DATA_SOURCE_SELECTOR_WIDTH = 400;
export const DATA_SOURCE_SELECTOR_WIDTH = 520;
export const showAllLogsLabel = i18n.translate(
'xpack.logsExplorer.dataSourceSelector.showAllLogs',
{ defaultMessage: 'Show all logs' }
);
export const addDataLabel = i18n.translate('xpack.logsExplorer.dataSourceSelector.addDataLabel', {
defaultMessage: 'Add data',
});
export const integrationsLabel = i18n.translate(
'xpack.logsExplorer.dataSourceSelector.integrations',
{ defaultMessage: 'Integrations' }

View file

@ -11,6 +11,8 @@ import React, { useState } from 'react';
import { I18nProvider } from '@kbn/i18n-react';
import type { Meta, Story } from '@storybook/react';
import { IndexPattern } from '@kbn/io-ts-utils';
import { CoreStart } from '@kbn/core/public';
import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public';
import { DataViewDescriptor } from '../../../common/data_views/models/data_view_descriptor';
import {
AllDatasetSelection,
@ -20,6 +22,7 @@ import {
import { Dataset, Integration } from '../../../common/datasets';
import { DataSourceSelector } from './data_source_selector';
import { DataSourceSelectorProps, DataSourceSelectorSearchParams } from './types';
import { IsDataViewAvailable } from '../../hooks/use_data_views';
const meta: Meta<typeof DataSourceSelector> = {
component: DataSourceSelector,
@ -42,6 +45,23 @@ const meta: Meta<typeof DataSourceSelector> = {
};
export default meta;
const coreMock = {
share: {
url: {
locators: {
get: () => {
return {
useUrl: () => 'http://localhost:5601/app/logs-explorer',
navigate: () => {},
};
},
},
},
},
} as unknown as CoreStart;
const KibanaReactContext = createKibanaReactContext(coreMock);
const DataSourceSelectorTemplate: Story<DataSourceSelectorProps> = (args) => {
const [dataSourceSelection, setDataSourceSelection] = useState<DataSourceSelection>(() =>
AllDatasetSelection.create()
@ -63,6 +83,10 @@ const DataSourceSelectorTemplate: Story<DataSourceSelectorProps> = (args) => {
setDataSourceSelection(newSelection);
};
const isDataViewAvailable: IsDataViewAvailable = (dataView) => {
return true;
};
const filteredIntegrations = integrations.filter((integration) =>
integration.name.includes(search.name as string)
);
@ -93,23 +117,26 @@ const DataSourceSelectorTemplate: Story<DataSourceSelectorProps> = (args) => {
} = args;
return (
<DataSourceSelector
{...args}
datasets={Boolean(datasetsError || isLoadingUncategorized) ? [] : sortedDatasets}
dataViews={Boolean(dataViewsError || isLoadingDataViews) ? [] : sortedDataViews}
dataSourceSelection={dataSourceSelection}
integrations={Boolean(integrationsError || isLoadingIntegrations) ? [] : sortedIntegrations}
onDataViewsSearch={setSearch}
onDataViewsSort={setSearch}
onIntegrationsLoadMore={onIntegrationsLoadMore}
onIntegrationsSearch={setSearch}
onIntegrationsSort={setSearch}
onIntegrationsStreamsSearch={setSearch}
onIntegrationsStreamsSort={setSearch}
onSelectionChange={onSelectionChange}
onUncategorizedSearch={setSearch}
onUncategorizedSort={setSearch}
/>
<KibanaReactContext.Provider>
<DataSourceSelector
{...args}
datasets={Boolean(datasetsError || isLoadingUncategorized) ? [] : sortedDatasets}
dataViews={Boolean(dataViewsError || isLoadingDataViews) ? [] : sortedDataViews}
dataSourceSelection={dataSourceSelection}
integrations={Boolean(integrationsError || isLoadingIntegrations) ? [] : sortedIntegrations}
isDataViewAvailable={isDataViewAvailable}
onDataViewsSearch={setSearch}
onDataViewsSort={setSearch}
onIntegrationsLoadMore={onIntegrationsLoadMore}
onIntegrationsSearch={setSearch}
onIntegrationsSort={setSearch}
onIntegrationsStreamsSearch={setSearch}
onIntegrationsStreamsSort={setSearch}
onSelectionChange={onSelectionChange}
onUncategorizedSearch={setSearch}
onUncategorizedSort={setSearch}
/>
</KibanaReactContext.Provider>
);
};

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import { EuiContextMenu, EuiHorizontalRule, EuiTab, EuiTabs } from '@elastic/eui';
import { EuiContextMenu, EuiFlexGroup, EuiHorizontalRule, EuiTab, EuiTabs } from '@elastic/eui';
import styled from '@emotion/styled';
import React, { useMemo } from 'react';
import { useIntersectionRef } from '../../hooks/use_intersection_ref';
@ -34,6 +34,7 @@ import {
createIntegrationStatusItem,
createUncategorizedStatusItem,
} from './utils';
import { AddDataButton } from './sub_components/add_data_button';
export function DataSourceSelector({
datasets,
@ -221,7 +222,10 @@ export function DataSourceSelector({
closePopover={closePopover}
onClick={togglePopover}
>
<Tabs>{tabEntries}</Tabs>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<Tabs bottomBorder={false}>{tabEntries}</Tabs>
{(tabId === INTEGRATIONS_TAB_ID || tabId === UNCATEGORIZED_TAB_ID) && <AddDataButton />}
</EuiFlexGroup>
<EuiHorizontalRule margin="none" />
<SearchControls
key={panelId}

View file

@ -0,0 +1,44 @@
/*
* 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 { EuiButtonEmpty } from '@elastic/eui';
import {
OBSERVABILITY_ONBOARDING_LOCATOR,
ObservabilityOnboardingLocatorParams,
} from '@kbn/deeplinks-observability';
import { getRouterLinkProps } from '@kbn/router-utils';
import React from 'react';
import { addDataLabel } from '../constants';
import { useKibanaContextForPlugin } from '../../../utils/use_kibana';
export const AddDataButton: React.FunctionComponent<{}> = ({}) => {
const {
services: {
share: { url: urlService },
},
} = useKibanaContextForPlugin();
const locator = urlService.locators.get<ObservabilityOnboardingLocatorParams>(
OBSERVABILITY_ONBOARDING_LOCATOR
);
const onboardingUrl = locator?.useUrl({});
const navigateToOnboarding = () => {
locator?.navigate({});
};
const onboardingLinkProps = getRouterLinkProps({
href: onboardingUrl,
onClick: navigateToOnboarding,
});
return (
<EuiButtonEmpty {...onboardingLinkProps} iconType="plusInCircleFilled" size="xs">
{addDataLabel}
</EuiButtonEmpty>
);
};

View file

@ -8,7 +8,7 @@ import type { ComponentType } from 'react';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public';
import type { SharePluginSetup } from '@kbn/share-plugin/public';
import type { SharePluginSetup, SharePluginStart } from '@kbn/share-plugin/public';
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public';
import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
@ -36,4 +36,5 @@ export interface LogsExplorerStartDeps {
fieldFormats: FieldFormatsStart;
navigation: NavigationPublicPluginStart;
unifiedSearch: UnifiedSearchPublicPluginStart;
share: SharePluginStart;
}