Skip to content

Commit

Permalink
[Infrastructure UI] Hosts flyout storybook (#157200)
Browse files Browse the repository at this point in the history
Closes #156685

## Summary
This PR adds stories for the flyout components. It includes storybooks
for the separate components inside the flyout.
The components will be moved to an embeddable and I decided to keep the
stories separate to ensure that they will still work in another context
(different than a flyout).

_Update_ After some discussions I also added a storybook for the Flyout
inside the host view (it will stay there after the content is moved to
an embeddable) It will include 2 stories showing 2 tabs (metadata and
processes).
I couldn't find a good way to display the flyout always on the page (if
it's always open it will hide the docs section after switching). I tried
to search for a storybook implementation of a flyout but couldn't find a
"great" example. At the end, I added an extra button on the page inside
the story template to "open" it:

<img width="2221" alt="image"
src="https://github.com/elastic/kibana/assets/14139027/7ddc5c32-50d3-4e34-8c79-ace1c8c6ee1d">

## Additional changes
- Added a wrapper around the flyout component
- Why do we need it?: So the flyout is a complex component and the logic
there is depending on URL data and has child components depending on the
data provided there. So having the wrapper will help us "mock" the props
easier in the storybook.


## Known issues 🐞 
- As we are moving those components to embeddable (in this issue) and we
are going to use lens charts currently I didn't invest time to make the
charts work as we are probably changing them soon anyway (this can still
be changed once it's already moved to embeddable)

<img width="2523" alt="image"
src="https://github.com/elastic/kibana/assets/14139027/1c604766-9481-47be-9c11-2debfabe35af">


- The storybook addons warning
[here](https://github.com/elastic/kibana/pull/157200/files#diff-93476b1ed21e5574f018cb4ebd6f774723725ef74880c5736e83332e399d2ffcR10-R12)
won't be an issue once it's moved to embeddable

- Closing the flyout: In general, having a complex component like the
host details flyout in a single story is a bit tricky to implement
(which I realized after trying many options to do that and there are
always some issues) As it's not a straightforward flag like we have for
the loading for example ( it depends on the data provided). I found
[actionArgs](https://storybook.js.org/docs/react/essentials/actions#action-args)
examples but they are used to display data received by the event handler
and not to execute extra logic from what I understood. Overall most of
the storybook examples are pretty 'static' which is fine keeping in mind
that the idea of the storybook is not to implement functionality.
My idea to add an option to close the flyout is to avoid "hiding" the
page content behind it in case of checking the docs. The workaround with
an open button helps with the situation where the documentation and the
actual page are still not hidden behind the flyout before the button is
clicked, but closing it still has issues (showing a blank docs screen
after closing, needs a page refresh or switching to a different story to
fix it)


## Testing
- Run `yarn storybook infra` in the main project folder
- Open the url shown in the terminal (probably http://localhost:9001/ or
http://localhost:9002/)
- Expand the Host Details View and check the stories there:


https://github.com/elastic/kibana/assets/14139027/9dda88ec-642b-4a94-a8d4-4b31081e9d5a

Loading toggle:


https://github.com/elastic/kibana/assets/14139027/9d3803a8-a160-47f0-9f29-9604d42ac9be


https://github.com/elastic/kibana/assets/14139027/77536075-a821-40dd-a659-40a6477ddf2e


- Other option: Storybook Build link:
https://ci-artifacts.kibana.dev/storybooks/pr-157200/0ec8068c5ed3cb0bb7cd8822b7c606f48ec8d4bf/infra/index.html?path=/docs/infra-host-details-view-components-processes--default-processes-and-summary
  • Loading branch information
jennypavlova committed May 15, 2023
1 parent e610ba5 commit b7575fc
Show file tree
Hide file tree
Showing 15 changed files with 904 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.
*/

/* eslint-disable @kbn/telemetry/event_generating_elements_should_be_instrumented */

import { EuiButton, EuiCard } from '@elastic/eui';
import { I18nProvider } from '@kbn/i18n-react';
import type { Meta, Story } from '@storybook/react/types-6-0';
import React from 'react';
import { decorateWithGlobalStorybookThemeProviders } from '../../../../../test_utils/use_global_storybook_theme';
import { DecorateWithKibanaContext } from './flyout.story_decorators';

import { Flyout, type FlyoutProps } from './flyout';

export default {
title: 'infra/Host Details View/Flyout',
decorators: [
(wrappedStory) => <EuiCard title="Host Details Flyout">{wrappedStory()}</EuiCard>,
(wrappedStory) => <I18nProvider>{wrappedStory()}</I18nProvider>,
decorateWithGlobalStorybookThemeProviders,
DecorateWithKibanaContext,
],
component: Flyout,
args: {
node: {
name: 'host1',
id: 'host1-macOS',
title: {
name: 'host1',
cloudProvider: null,
},
os: 'macOS',
ip: '192.168.0.1',
rx: 123179.18222222221,
tx: 123030.54555555557,
memory: 0.9044444444444445,
cpu: 0.3979674157303371,
diskLatency: 0.15291777273162221,
memoryTotal: 34359738368,
},
closeFlyout: () => {},
onTabClick: () => {},
renderedTabsSet: { current: new Set(['metadata']) },
currentTimeRange: {
interval: '1s',
from: 1683630468,
to: 1683630469,
},
hostFlyoutOpen: {
clickedItemId: 'host1-macos',
selectedTabId: 'metadata',
searchFilter: '',
metadataSearch: '',
},
},
} as Meta;

const Template: Story<FlyoutProps> = (args) => {
const [isOpen, setIsOpen] = React.useState(false);
const closeFlyout = () => setIsOpen(false);
return (
<div>
<EuiButton onClick={() => setIsOpen(true)}>Open flyout</EuiButton>
{isOpen && <Flyout {...args} closeFlyout={closeFlyout} />}
</div>
);
};

export const DefaultFlyoutMetadata = Template.bind({});
DefaultFlyoutMetadata.args = {};

export const FlyoutWithProcesses = Template.bind({});
FlyoutWithProcesses.args = {
renderedTabsSet: { current: new Set(['processes']) },
currentTimeRange: {
interval: '1s',
from: 1683630468,
to: 1683630469,
},
hostFlyoutOpen: {
clickedItemId: 'host1-macos',
selectedTabId: 'processes',
searchFilter: '',
metadataSearch: '',
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/*
* 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 { StoryContext } from '@storybook/react';
import React from 'react';
import { I18nProvider } from '@kbn/i18n-react';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { SourceProvider } from '../../../../../containers/metrics_source';

export const DecorateWithKibanaContext = <StoryFnReactReturnType extends React.ReactNode>(
wrappedStory: () => StoryFnReactReturnType,
_storyContext: StoryContext
) => {
const processesResponse = {
processList: [
{
cpu: 0.02466666666666667,
memory: 0.026166666666666668,
startTime: 1683624717239,
pid: 757,
state: 'running',
user: 'test_user',
command: '/Applications/Firefox.app/Contents/MacOS/firefox',
},
{
cpu: 0.006833333333333334,
memory: 0.07200000000000001,
startTime: 1683624734638,
pid: 3524,
state: 'running',
user: 'test_user',
command:
'/Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper (Plugin).app/Contents/MacOS/Code Helper (Plugin) --ms-enable-electron-run-as-node --max-old-space-size=4096 /Users/test_user/projects/forks/kibana/node_modules/typescript/lib/tsserver.js --useInferredProjectPerProjectRoot --enableTelemetry --cancellationPipeName /var/folders/hq/pz_mbrf55lg_r37lr3l0n6nr0000gn/T/vscode-typescript501/b23862414d9a371466ef/tscancellation-9cbfb32954f6e79a5287.tmp* --locale en --noGetErrOnBackgroundUpdate --validateDefaultNpmLocation --useNodeIpc',
},
{
cpu: 0.006666666666666667,
memory: 0.012000000000000002,
startTime: 1683633422827,
pid: 12355,
state: 'running',
user: 'test_user',
command:
'/Applications/Firefox.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container -childID 20 -isForBrowser -prefsLen 29966 -prefMapSize 241364 -jsInitLen 240056 -sbStartup -sbAppPath /Applications/Firefox.app -sbLevel 3 -sbAllowAudio -parentBuildID 20230424110519 -appDir /Applications/Firefox.app/Contents/Resources/browser -profile /Users/test_user/Library/Application Support/Firefox/Profiles/rqulcocl.default-release {c336b12a-aaaa-aaaa-aaaa-d9f1a22b24b9} 757 gecko-crash-server-pipe.757 org.mozilla.machname.1351433086 tab',
},
{
cpu: 0.0030000000000000005,
memory: 0.014,
startTime: 1683625026636,
pid: 6474,
state: 'running',
user: 'test_user',
command:
'/Users/test_user/.vscode/extensions/ambar.bundle-size-1.5.0/node_modules/esbuild-darwin-arm64/bin/esbuild --service=0.15.18 --ping',
},
{
cpu: 0.0025,
memory: 0.016,
startTime: 1683624729210,
pid: 3034,
state: 'running',
user: 'test_user',
command:
'/Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper (Renderer).app/Contents/MacOS/Code Helper (Renderer) --type=renderer --user-data-dir=/Users/test_user/Library/Application Support/Code --standard-schemes=vscode-webview,vscode-file --secure-schemes=vscode-webview,vscode-file --bypasscsp-schemes --cors-schemes=vscode-webview,vscode-file --fetch-schemes=vscode-webview,vscode-file --service-worker-schemes=vscode-webview --streaming-schemes --app-path=/Applications/Visual Studio Code.app/Contents/Resources/app --no-sandbox --no-zygote --enable-blink-features=HighlightAPI --lang=en-GB --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=8 --launch-time-ticks=267710381 --shared-files --field-trial-handle=1718379636,r,13842708672762445701,9157690858550926405,131072 --disable-features=CalculateNativeWinOcclusion,SpareRendererForSitePerProcess --vscode-window-config=vscode:c40ae943-cf5f-4b4e-9ede-714d3202da26',
},
{
cpu: 0.0013333333333333333,
memory: 0.011666666666666667,
startTime: 1683628721310,
pid: 11385,
state: 'running',
user: 'test_user',
command:
'/Applications/Slack.app/Contents/Frameworks/Slack Helper (Renderer).app/Contents/MacOS/Slack Helper (Renderer) --type=renderer --user-data-dir=/Users/test_user/Library/Application Support/Slack --standard-schemes=app,slack-webapp-dev --enable-sandbox --secure-schemes=app,slack-webapp-dev --bypasscsp-schemes=slack-webapp-dev --cors-schemes=slack-webapp-dev --fetch-schemes=slack-webapp-dev --service-worker-schemes=slack-webapp-dev --streaming-schemes --app-path=/Applications/Slack.app/Contents/Resources/app.asar --enable-sandbox --enable-blink-features=ExperimentalJSProfiler --disable-blink-features --first-renderer-process --autoplay-policy=no-user-gesture-required --enable-logging --force-color-profile=srgb --log-file=/Users/test_user/Library/Application Support/Slack/logs/default/electron_debug.log --lang=en-GB --num-raster-threads=4 --enable-zero-copy --enable-gpu-memory-buffer-compositor-resources --enable-main-frame-before-activation --renderer-client-id=4 --time-ticks-at-unix-epoch=-1683624461530404 --launch-time-ticks=4259772827 --shared-files --field-trial-handle=1718379636,r,7616643094726622586,3291986448361336128,131072 --disable-features=AllowAggressiveThrottlingWithWebSocket,CalculateNativeWinOcclusion,HardwareMediaKeyHandling,IntensiveWakeUpThrottling,LogJsConsoleMessages,RequestInitiatorSiteLockEnfocement,SpareRendererForSitePerProcess,WebRtcHideLocalIpsWithMdns,WinRetrieveSuggestionsOnlyOnDemand --window-type=main --seatbelt-client=71',
},
{
cpu: 0.0013333333333333333,
memory: 0.0105,
startTime: 1683624737323,
pid: 3593,
state: 'running',
user: 'test_user',
command:
'/Applications/Visual Studio Code.app/Contents/Frameworks/Code Helper (Plugin).app/Contents/MacOS/Code Helper (Plugin) --ms-enable-electron-run-as-node /Users/test_user/.vscode/extensions/streetsidesoftware.code-spell-checker-2.20.4/packages/_server/dist/main.js --node-ipc --clientProcessId=3508',
},
{
cpu: 0.0011666666666666668,
memory: 0.014666666666666668,
startTime: 1683625569286,
pid: 8319,
state: 'running',
user: 'test_user',
command:
'/Applications/Firefox.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container -childID 14 -isForBrowser -prefsLen 29644 -prefMapSize 241364 -jsInitLen 240056 -sbStartup -sbAppPath /Applications/Firefox.app -sbLevel 3 -sbAllowAudio -parentBuildID 20230424110519 -appDir /Applications/Firefox.app/Contents/Resources/browser -profile /Users/test_user/Library/Application Support/Firefox/Profiles/rqulcocl.default-release {49cd6e6d-ed04-4355-8a7c-9a13fb9cbfa8} 757 gecko-crash-server-pipe.757 org.mozilla.machname.1880877485 tab',
},
{
cpu: 0.001,
memory: 0.0030000000000000005,
startTime: 1683627994731,
pid: 10269,
state: 'running',
user: 'test_user',
command: './metricbeat -v -e -c metricbeat.dev.yml',
},
{
cpu: 0.001,
memory: 0.006000000000000001,
startTime: 1683624717742,
pid: 784,
state: 'running',
user: 'test_user',
command: '/Applications/Visual Studio Code.app/Contents/MacOS/Electron',
},
],
summary: { running: 366, total: 366 },
};

const metadataResponse = {
id: 'host1',
name: 'host1',
info: {
cloud: {
availability_zone: 'us-central1-c',
instance: {
name: 'host2',
id: '1234567891234567896',
},
provider: 'gcp',
service: {
name: 'GCE',
},
machine: {
type: 'e2-machine',
},
project: {
id: 'some-project',
},
account: {
id: 'some-project',
},
},
agent: {
name: 'host2',
id: '1a3s3f5g-2222-aaaa-1d35-fd34133t241',
ephemeral_id: 'fdaf43q5-rwe3-ee22-6666-fdsfhwy34535',
type: 'metricbeat',
version: '8.8.0',
},
host: {
hostname: 'host2',
os: {
build: '1111.1111',
kernel: '10.0.1111.1111 (WinBuild.160101.0800)',
name: 'Windows Server 2019 Datacenter',
family: 'windows',
type: 'windows',
version: '10.0',
platform: 'windows',
},
ip: ['as98::111r:aab2s:w123:v00s', '192.168.0.25'],
name: 'host2',
id: '1245t34f-aaaa-6666-1111-5555666777722',
mac: ['66-66-0A-66-00-66'],
architecture: 'x86_64',
},
},
features: [],
};

const mockServices = {
application: {
currentAppId$: { title: 'infra', subscribe: () => {} },
navigateToUrl: () => {},
},
data: {
query: {
filterManager: { filterManagerService: { addFilters: () => {}, removeFilter: () => {} } },
},
},
notifications: { toasts: { add: () => {}, toastsService: { addSuccess: () => {} } } },
telemetry: () => {},
http: {
basePath: {
prepend: (_: string) => '',
},
patch: () => {},
fetch: async (path: string) => {
switch (path) {
case '/api/infra/metadata':
return metadataResponse;
case '/api/metrics/process_list':
return { ...processesResponse, loading: false };
default:
return {};
}
},
},
share: {
url: {
locators: {
get: () => ({
navigate: () =>
`https://kibana:8080/base-path/app/uptime/?search=host.name: "host1" OR host.ip: "192.168.0.1" OR monitor.ip: "192.168.0.1"`,
}),
},
},
},
};

return (
<I18nProvider>
<KibanaContextProvider services={mockServices}>
<SourceProvider sourceId="default">{wrappedStory()} </SourceProvider>
</KibanaContextProvider>
</I18nProvider>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,55 +13,60 @@ import {
EuiFlyoutBody,
EuiFlexGroup,
EuiFlexItem,
EuiTab,
EuiSpacer,
EuiTabs,
EuiTab,
useEuiTheme,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { useUnifiedSearchContext } from '../../hooks/use_unified_search';
import { LinkToUptime } from './links/link_to_uptime';
import { LinkToApmServices } from './links/link_to_apm_services';
import { useLazyRef } from '../../../../../hooks/use_lazy_ref';
import { metadataTab } from './metadata';
import type { InventoryItemType } from '../../../../../../common/inventory_models/types';
import type { HostNodeRow } from '../../hooks/use_hosts_table';
import { processesTab } from './processes';
import { Metadata } from './metadata/metadata';
import { Processes } from './processes/processes';
import { FlyoutTabIds, useHostFlyoutOpen } from '../../hooks/use_host_flyout_open_url_state';
import { FlyoutTabIds } from '../../hooks/use_host_flyout_open_url_state';
import type { Tab } from './flyout_wrapper';
import { metadataTab } from './metadata';
import { processesTab } from './processes';

interface Props {
type FLYOUT_TABS = 'metadata' | 'processes';
export interface FlyoutProps {
node: HostNodeRow;
closeFlyout: () => void;
renderedTabsSet: React.MutableRefObject<Set<FLYOUT_TABS>>;
currentTimeRange: {
interval: string;
from: number;
to: number;
};
hostFlyoutOpen: {
clickedItemId: string;
selectedTabId: FLYOUT_TABS;
searchFilter: string;
metadataSearch: string;
};
onTabClick: (tab: Tab) => void;
}

const flyoutTabs = [metadataTab, processesTab];
const NODE_TYPE = 'host' as InventoryItemType;
const flyoutTabs: Tab[] = [metadataTab, processesTab];

export const Flyout = ({ node, closeFlyout }: Props) => {
const { getDateRangeAsTimestamp } = useUnifiedSearchContext();
export const Flyout = ({
node,
closeFlyout,
onTabClick,
renderedTabsSet,
currentTimeRange,
hostFlyoutOpen,
}: FlyoutProps) => {
const { euiTheme } = useEuiTheme();

const currentTimeRange = {
...getDateRangeAsTimestamp(),
interval: '1m',
};

const [hostFlyoutOpen, setHostFlyoutOpen] = useHostFlyoutOpen();

// This map allow to keep track of which tabs content have been rendered the first time.
// We need it in order to load a tab content only if it gets clicked, and then keep it in the DOM for performance improvement.
const renderedTabsSet = useLazyRef(() => new Set([hostFlyoutOpen.selectedTabId]));

const tabEntries = flyoutTabs.map((tab) => (
<EuiTab
{...tab}
key={tab.id}
onClick={() => {
renderedTabsSet.current.add(tab.id); // On a tab click, mark the tab content as allowed to be rendered
setHostFlyoutOpen({ selectedTabId: tab.id });
}}
onClick={() => onTabClick(tab)}
isSelected={tab.id === hostFlyoutOpen.selectedTabId}
>
{tab.name}
Expand Down
Loading

0 comments on commit b7575fc

Please sign in to comment.