mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[ObsUX] Asset details storybook fixes (#170262)
Closes #169123 ## Summary This PR fixes the asset details storybook. There are a lot of changes we did that broke the existing stories - adding the feature flags, using the URL state to select the active tab, using new services/hooks, etc. The order of the decorators was also returning an error in the component stories. The background of the tab switching issue is that the active tab is set in the URL and we can't use the query params here (they won't be set in the storybook in case a different tab is selected) As a workaround for the tab selection issue, I added an extra `arg` to see all tab names and to select one of them - it's not perfect but I was trying to avoid changing the tab switcher code only to make the story work. One issue with that is after selecting a value in the controls the canvas is not refreshed so I added different stories for different tabs. It works in Docs mode:aa6ec610
-14b9-48c3-a7e4-02f57bac9390 That's the result so far:d7eb6ed0
-f655-4576-9480-b096f5aec007 ## Next steps 🐾 The stories can be useful even if not everything inside the component is available and the mocks do not cover every case. Supporting more cases can become tricky so we can keep it in this minimum component view and have the tabs available as part of the asset details story ( flyout and page ). Tab switching can be improved as currently they can be switched only using the Story Controls, flyout dropdown in the story, or the left navigation (clicking on them won't work) - I explained that in the PRs summary. We can also decide if we want to keep the stories for the asset details as they are pretty easy to break. If we decide to support it in the future we should keep it in mind while testing PRs. ## Update on found issues 🔍 Other issues I see (currently not fixed) are: - Anomalies are not rendered for the default case - Update: I tried different things and I saw that the date change is helping the anomalies to show (on initial load they never show - but after a date change using the date picker they appear, even when the same date is selected:ce0461cc
-4c2c-4134-96dc-829b480a5be1 It's really odd and I couldn't find a solution for that - As a workaround, I removed the anomalies component story as it won't work without the date picker. - Logs Tab data is not visible (even without data the tab renders without errors) - Process list: when clicking on a process in the list there is an error shown (related AI feature context) - Update: This is fixed - the AI assistant component will be visible (not functional) <img width="1916" alt="image" src="9d41484d
-506c-4f5d-8940-57010ec11303"> ## Testing - The easy way is to click on [Storybooks Preview](https://ci-artifacts.kibana.dev/storybooks/pr-170262/cd96eb6d6da95fa34c9b2582c353b7ecd6e80b66) inside the PR build section - To see the stories locally run `yarn storybook infra` and open http://localhost:9001/
This commit is contained in:
parent
55d94c772a
commit
4557ac83e0
7 changed files with 144 additions and 79 deletions
|
@ -16,7 +16,7 @@ const anomalies: GetMetricsHostsAnomaliesSuccessResponsePayload = {
|
|||
actual: 758.8220213274412,
|
||||
anomalyScore: 0.024881740359975164,
|
||||
duration: 900,
|
||||
startTime: 1486845000000,
|
||||
startTime: 1681038638000,
|
||||
type: 'metrics_hosts',
|
||||
partitionFieldName: 'airline',
|
||||
partitionFieldValue: 'NKS',
|
||||
|
@ -42,7 +42,7 @@ const anomalies: GetMetricsHostsAnomaliesSuccessResponsePayload = {
|
|||
actual: 758.8220213274412,
|
||||
anomalyScore: 100.024881740359975164,
|
||||
duration: 900,
|
||||
startTime: 1486845000000,
|
||||
startTime: 1681038638000,
|
||||
type: 'metrics_hosts',
|
||||
partitionFieldName: 'airline',
|
||||
partitionFieldValue: 'NKS',
|
||||
|
|
|
@ -16,12 +16,6 @@ const tabs: Tab[] = [
|
|||
defaultMessage: 'Overview',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: ContentTabIds.LOGS,
|
||||
name: i18n.translate('xpack.infra.nodeDetails.tabs.logs', {
|
||||
defaultMessage: 'Logs',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: ContentTabIds.METADATA,
|
||||
name: i18n.translate('xpack.infra.metrics.nodeDetails.tabs.metadata', {
|
||||
|
@ -34,6 +28,12 @@ const tabs: Tab[] = [
|
|||
defaultMessage: 'Processes',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: ContentTabIds.LOGS,
|
||||
name: i18n.translate('xpack.infra.nodeDetails.tabs.logs', {
|
||||
defaultMessage: 'Logs',
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: ContentTabIds.ANOMALIES,
|
||||
name: i18n.translate('xpack.infra.nodeDetails.tabs.anomalies', {
|
||||
|
|
|
@ -11,24 +11,33 @@ import {
|
|||
KibanaContextProvider,
|
||||
type KibanaReactContextValue,
|
||||
} from '@kbn/kibana-react-plugin/public';
|
||||
import { of } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import type { DecoratorFn } from '@storybook/react';
|
||||
import { useParameter } from '@storybook/addons';
|
||||
import type { DeepPartial } from 'utility-types';
|
||||
import type { LocatorPublic } from '@kbn/share-plugin/public';
|
||||
import type { IKibanaSearchRequest, ISearchOptions } from '@kbn/data-plugin/public';
|
||||
import type {
|
||||
IKibanaSearchRequest,
|
||||
ISearchOptions,
|
||||
SearchSessionState,
|
||||
} from '@kbn/data-plugin/public';
|
||||
import { AlertSummaryWidget } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget/alert_summary_widget';
|
||||
import type { Theme } from '@elastic/charts/dist/utils/themes/theme';
|
||||
import type { AlertSummaryWidgetProps } from '@kbn/triggers-actions-ui-plugin/public/application/sections/alert_summary_widget';
|
||||
import { defaultLogViewAttributes } from '@kbn/logs-shared-plugin/common';
|
||||
import { DataView, DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public';
|
||||
import { ObservabilityAIAssistantService } from '@kbn/observability-ai-assistant-plugin/public/types';
|
||||
import { PluginConfigProvider } from '../../../containers/plugin_config_context';
|
||||
import type { PluginKibanaContextValue } from '../../../hooks/use_kibana';
|
||||
import { SourceProvider } from '../../../containers/metrics_source';
|
||||
import { getHttp } from './context/http';
|
||||
import { assetDetailsProps, getLogEntries } from './context/fixtures';
|
||||
import { ContextProviders } from '../context_providers';
|
||||
import { DataViewsProvider } from '../hooks/use_data_views';
|
||||
import type { InfraConfig } from '../../../../server';
|
||||
|
||||
const settings: Record<string, any> = {
|
||||
'dateFormat:scaled': [['', 'HH:mm:ss.SSS']],
|
||||
|
@ -58,6 +67,10 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => {
|
|||
search: (request: IKibanaSearchRequest, options?: ISearchOptions) => {
|
||||
return getLogEntries(request, options) as any;
|
||||
},
|
||||
session: {
|
||||
start: () => 'started',
|
||||
state$: { closed: false } as unknown as Observable<SearchSessionState>,
|
||||
},
|
||||
},
|
||||
query: {
|
||||
filterManager: {
|
||||
|
@ -144,14 +157,64 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => {
|
|||
},
|
||||
telemetry: {
|
||||
reportAssetDetailsFlyoutViewed: () => {},
|
||||
reportAssetDetailsPageViewed: () => {},
|
||||
},
|
||||
};
|
||||
|
||||
const config: InfraConfig = {
|
||||
alerting: {
|
||||
inventory_threshold: {
|
||||
group_by_page_size: 11,
|
||||
},
|
||||
metric_threshold: {
|
||||
group_by_page_size: 11,
|
||||
},
|
||||
},
|
||||
enabled: true,
|
||||
inventory: {
|
||||
compositeSize: 11,
|
||||
},
|
||||
sources: {
|
||||
default: {
|
||||
fields: {
|
||||
message: ['default'],
|
||||
},
|
||||
},
|
||||
},
|
||||
featureFlags: {
|
||||
customThresholdAlertsEnabled: true,
|
||||
logsUIEnabled: false,
|
||||
metricsExplorerEnabled: false,
|
||||
osqueryEnabled: true,
|
||||
inventoryThresholdAlertRuleEnabled: true,
|
||||
metricThresholdAlertRuleEnabled: true,
|
||||
logThresholdAlertRuleEnabled: true,
|
||||
alertsAndRulesDropdownEnabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<I18nProvider>
|
||||
<KibanaContextProvider services={mockServices}>
|
||||
<SourceProvider sourceId="default">{story()}</SourceProvider>
|
||||
</KibanaContextProvider>
|
||||
<MemoryRouter initialEntries={['/infra/metrics/hosts']}>
|
||||
<PluginConfigProvider value={config}>
|
||||
<KibanaContextProvider services={mockServices}>
|
||||
<ObservabilityAIAssistantProvider
|
||||
value={
|
||||
{
|
||||
isEnabled: () => true,
|
||||
callApi: () => {},
|
||||
getCurrentUser: () => {},
|
||||
getLicense: () => {},
|
||||
getLicenseManagementLocator: () => {},
|
||||
start: {},
|
||||
} as unknown as ObservabilityAIAssistantService
|
||||
}
|
||||
>
|
||||
<SourceProvider sourceId="default">{story()}</SourceProvider>
|
||||
</ObservabilityAIAssistantProvider>
|
||||
</KibanaContextProvider>
|
||||
</PluginConfigProvider>
|
||||
</MemoryRouter>
|
||||
</I18nProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,19 +6,32 @@
|
|||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { EuiButton, EuiCallOut, EuiSelect, EuiSpacer } from '@elastic/eui';
|
||||
import type { Meta, Story } from '@storybook/react/types-6-0';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { useArgs } from '@storybook/addons';
|
||||
import { AssetDetails } from './asset_details';
|
||||
import { decorateWithGlobalStorybookThemeProviders } from '../../test_utils/use_global_storybook_theme';
|
||||
import { type AssetDetailsProps } from './types';
|
||||
import { type TabIds, type AssetDetailsProps } from './types';
|
||||
import { DecorateWithKibanaContext } from './__stories__/decorator';
|
||||
import { assetDetailsProps } from './__stories__/context/fixtures';
|
||||
|
||||
const stories: Meta<AssetDetailsProps> = {
|
||||
interface AssetDetailsStoryArgs extends AssetDetailsProps {
|
||||
tabId: TabIds;
|
||||
}
|
||||
|
||||
const stories: Meta<AssetDetailsStoryArgs> = {
|
||||
title: 'infra/Asset Details View',
|
||||
decorators: [decorateWithGlobalStorybookThemeProviders, DecorateWithKibanaContext],
|
||||
component: AssetDetails,
|
||||
argTypes: {
|
||||
tabId: {
|
||||
options: assetDetailsProps.tabs.filter(({ id }) => id !== 'linkToApm').map(({ id }) => id),
|
||||
defaultValue: 'overview',
|
||||
control: {
|
||||
type: 'radio',
|
||||
},
|
||||
},
|
||||
links: {
|
||||
options: assetDetailsProps.links,
|
||||
control: {
|
||||
|
@ -26,20 +39,42 @@ const stories: Meta<AssetDetailsProps> = {
|
|||
},
|
||||
},
|
||||
},
|
||||
args: {
|
||||
...assetDetailsProps,
|
||||
},
|
||||
args: { ...assetDetailsProps },
|
||||
};
|
||||
|
||||
const PageTemplate: Story<AssetDetailsProps> = (args) => {
|
||||
return <AssetDetails {...args} />;
|
||||
const PageTabTemplate: Story<AssetDetailsStoryArgs> = (args) => {
|
||||
return (
|
||||
<MemoryRouter initialEntries={[`/infra/metrics/hosts?assetDetails=(tabId:${args.tabId})`]}>
|
||||
<AssetDetails {...args} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
const FlyoutTemplate: Story<AssetDetailsProps> = (args) => {
|
||||
const FlyoutTemplate: Story<AssetDetailsStoryArgs> = (args) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const closeFlyout = () => setIsOpen(false);
|
||||
const options = assetDetailsProps.tabs.filter(({ id }) => id !== 'linkToApm').map(({ id }) => id);
|
||||
const [{ tabId }, updateArgs] = useArgs();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
title={`To see different tab content please close the flyout if opened, select one of the options from the drop-down and open the flyout again:`}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiSelect
|
||||
data-test-subj="infraFlyoutTemplateSelect"
|
||||
value={tabId}
|
||||
onChange={(e) => {
|
||||
updateArgs({ tabId: e.target.value as TabIds });
|
||||
}}
|
||||
options={options.map((id) => ({
|
||||
text: id,
|
||||
value: id,
|
||||
}))}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
<EuiButton
|
||||
data-test-subj="infraFlyoutTemplateOpenFlyoutButton"
|
||||
onClick={() => setIsOpen(true)}
|
||||
|
@ -47,13 +82,33 @@ const FlyoutTemplate: Story<AssetDetailsProps> = (args) => {
|
|||
Open flyout
|
||||
</EuiButton>
|
||||
<div hidden={!isOpen}>
|
||||
{isOpen && <AssetDetails {...args} renderMode={{ mode: 'flyout', closeFlyout }} />}
|
||||
{isOpen && (
|
||||
<MemoryRouter
|
||||
key={tabId}
|
||||
initialEntries={[`/infra/metrics/hosts?assetDetails=(tabId:${tabId ?? args?.tabId})`]}
|
||||
>
|
||||
<AssetDetails {...args} renderMode={{ mode: 'flyout', closeFlyout }} />
|
||||
</MemoryRouter>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Page = PageTemplate.bind({});
|
||||
export const OverviewTab = PageTabTemplate.bind({});
|
||||
OverviewTab.args = { tabId: 'overview' };
|
||||
|
||||
export const MetadataTab = PageTabTemplate.bind({});
|
||||
MetadataTab.args = { tabId: 'metadata' };
|
||||
|
||||
export const ProcessesTab = PageTabTemplate.bind({});
|
||||
ProcessesTab.args = { tabId: 'processes' };
|
||||
|
||||
export const LogsTab = PageTabTemplate.bind({});
|
||||
LogsTab.args = { tabId: 'logs' };
|
||||
|
||||
export const AnomaliesTab = PageTabTemplate.bind({});
|
||||
AnomaliesTab.args = { tabId: 'anomalies' };
|
||||
|
||||
export const Flyout = FlyoutTemplate.bind({});
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* 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 type { Meta, Story } from '@storybook/react/types-6-0';
|
||||
|
||||
import { Anomalies } from './anomalies';
|
||||
import { decorateWithGlobalStorybookThemeProviders } from '../../../../test_utils/use_global_storybook_theme';
|
||||
import {
|
||||
DecorateWithKibanaContext,
|
||||
DecorateWithAssetDetailsStateContext,
|
||||
} from '../../__stories__/decorator';
|
||||
|
||||
const stories: Meta = {
|
||||
title: 'infra/Asset Details View/Components/Anomalies',
|
||||
decorators: [
|
||||
decorateWithGlobalStorybookThemeProviders,
|
||||
DecorateWithKibanaContext,
|
||||
DecorateWithAssetDetailsStateContext,
|
||||
],
|
||||
component: Anomalies,
|
||||
};
|
||||
|
||||
const Template: Story = () => {
|
||||
return <Anomalies />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
export const NoData = Template.bind({});
|
||||
NoData.parameters = {
|
||||
apiResponse: {
|
||||
mock: 'noData',
|
||||
},
|
||||
};
|
||||
|
||||
export const LoadingState = Template.bind({});
|
||||
LoadingState.parameters = {
|
||||
apiResponse: {
|
||||
mock: 'loading',
|
||||
},
|
||||
};
|
||||
|
||||
export default stories;
|
|
@ -18,8 +18,8 @@ const stories: Meta = {
|
|||
title: 'infra/Asset Details View/Components/Metadata',
|
||||
decorators: [
|
||||
decorateWithGlobalStorybookThemeProviders,
|
||||
DecorateWithKibanaContext,
|
||||
DecorateWithAssetDetailsStateContext,
|
||||
DecorateWithKibanaContext,
|
||||
],
|
||||
component: Metadata,
|
||||
};
|
||||
|
@ -30,11 +30,6 @@ const Template: Story = () => {
|
|||
|
||||
export const Default = Template.bind({});
|
||||
|
||||
export const WithActions = Template.bind({});
|
||||
WithActions.args = {
|
||||
showActionsColumn: true,
|
||||
};
|
||||
|
||||
export const NoData = Template.bind({});
|
||||
NoData.parameters = {
|
||||
apiResponse: {
|
||||
|
|
|
@ -18,8 +18,8 @@ const stories: Meta = {
|
|||
title: 'infra/Asset Details View/Components/Processes',
|
||||
decorators: [
|
||||
decorateWithGlobalStorybookThemeProviders,
|
||||
DecorateWithKibanaContext,
|
||||
DecorateWithAssetDetailsStateContext,
|
||||
DecorateWithKibanaContext,
|
||||
],
|
||||
component: Processes,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue