[Exploratory view] Allow ability add extra actions in lens embeddable (#123713)

This commit is contained in:
Shahzad 2022-01-28 11:15:49 +01:00 committed by GitHub
parent 9f6c78139e
commit 5b8af6c1ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 401 additions and 44 deletions

View file

@ -32,6 +32,7 @@ import type {
} from '../../../plugins/lens/public';
import { ViewMode } from '../../../../src/plugins/embeddable/public';
import { ActionExecutionContext } from '../../../../src/plugins/ui_actions/public';
// Generate a Lens state based on some app-specific input parameters.
// `TypedLensByValueInput` can be used for type-safety - it uses the same interfaces as Lens-internal code.
@ -126,6 +127,9 @@ export const App = (props: {
to: 'now',
});
const [enableExtraAction, setEnableExtraAction] = useState(false);
const [enableDefaultAction, setEnableDefaultAction] = useState(false);
const LensComponent = props.plugins.lens.EmbeddableComponent;
const LensSaveModalComponent = props.plugins.lens.SaveModalComponent;
@ -153,7 +157,7 @@ export const App = (props: {
configuration and navigate to a prefilled editor.
</p>
<EuiFlexGroup>
<EuiFlexGroup wrap>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj="lns-example-change-color"
@ -238,10 +242,34 @@ export const App = (props: {
Change time range
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Enable extra action"
data-test-subj="lns-example-extra-action"
isDisabled={!attributes}
onClick={() => {
setEnableExtraAction((prevState) => !prevState);
}}
>
{enableExtraAction ? 'Disable extra action' : 'Enable extra action'}
</EuiButton>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
aria-label="Enable default actions"
data-test-subj="lns-example-default-action"
isDisabled={!attributes}
onClick={() => {
setEnableDefaultAction((prevState) => !prevState);
}}
>
{enableDefaultAction ? 'Disable default action' : 'Enable default action'}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
<LensComponent
id=""
withActions
withDefaultActions={enableDefaultAction}
style={{ height: 500 }}
timeRange={time}
attributes={attributes}
@ -261,6 +289,27 @@ export const App = (props: {
// call back event for on table row click event
}}
viewMode={ViewMode.VIEW}
extraActions={
enableExtraAction
? [
{
id: 'testAction',
type: 'link',
getIconType: () => 'save',
async isCompatible(
context: ActionExecutionContext<object>
): Promise<boolean> {
return true;
},
execute: async (context: ActionExecutionContext<object>) => {
alert('I am an extra action');
return;
},
getDisplayName: () => 'Extra action',
},
]
: undefined
}
/>
{isSaveModalVisible && (
<LensSaveModalComponent

View file

@ -6,13 +6,14 @@
"server": false,
"ui": true,
"requiredPlugins": [
"observability",
"cases",
"data",
"developerExamples",
"embeddable",
"developerExamples"
"observability"
],
"optionalPlugins": [],
"requiredBundles": [],
"requiredBundles": ["kibanaReact"],
"owner": {
"name": "`Synthetics team`",
"githubTeam": "uptime"

View file

@ -80,6 +80,7 @@ export const App = (props: {
attributes={seriesList}
reportType="kpi-over-time"
title={'Monitor response duration'}
withActions={['save', 'explore']}
/>
</EuiPageContentBody>
</EuiPageContent>

View file

@ -7,9 +7,12 @@
import * as React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { CoreSetup, AppMountParameters } from 'kibana/public';
import { CoreSetup, AppMountParameters, APP_WRAPPER_CLASS } from '../../../../src/core/public';
import { StartDependencies } from './plugin';
import {
KibanaContextProvider,
RedirectAppLinks,
} from '../../../../src/plugins/kibana_react/public';
export const mount =
(coreSetup: CoreSetup<StartDependencies>) =>
async ({ element }: AppMountParameters) => {
@ -26,9 +29,13 @@ export const mount =
const i18nCore = core.i18n;
const reactElement = (
<i18nCore.Context>
<App {...deps} defaultIndexPattern={defaultIndexPattern} />
</i18nCore.Context>
<KibanaContextProvider services={{ ...coreSetup, ...core, ...plugins }}>
<i18nCore.Context>
<RedirectAppLinks application={core.application} className={APP_WRAPPER_CLASS}>
<App {...deps} defaultIndexPattern={defaultIndexPattern} />
</RedirectAppLinks>
</i18nCore.Context>
</KibanaContextProvider>
);
render(reactElement, element);
return () => unmountComponentAtNode(element);

View file

@ -7,7 +7,7 @@
import React, { FC, useEffect } from 'react';
import type { CoreStart, ThemeServiceStart } from 'kibana/public';
import type { UiActionsStart } from 'src/plugins/ui_actions/public';
import type { Action, UiActionsStart } from 'src/plugins/ui_actions/public';
import type { Start as InspectorStartContract } from 'src/plugins/inspector/public';
import { EuiLoadingChart } from '@elastic/eui';
import {
@ -52,7 +52,8 @@ export type TypedLensByValueInput = Omit<LensByValueInput, 'attributes'> & {
};
export type EmbeddableComponentProps = (TypedLensByValueInput | LensByReferenceInput) & {
withActions?: boolean;
withDefaultActions?: boolean;
extraActions?: Action[];
};
interface PluginsStartDependencies {
@ -67,7 +68,9 @@ export function getEmbeddableComponent(core: CoreStart, plugins: PluginsStartDep
const factory = embeddableStart.getEmbeddableFactory('lens')!;
const input = { ...props };
const [embeddable, loading, error] = useEmbeddableFactory({ factory, input });
const hasActions = props.withActions === true;
const hasActions =
Boolean(props.withDefaultActions) || (props.extraActions && props.extraActions?.length > 0);
const theme = core.theme;
if (loading) {
@ -83,6 +86,8 @@ export function getEmbeddableComponent(core: CoreStart, plugins: PluginsStartDep
actionPredicate={() => hasActions}
input={input}
theme={theme}
extraActions={props.extraActions}
withDefaultActions={props.withDefaultActions}
/>
);
}
@ -98,6 +103,8 @@ interface EmbeddablePanelWrapperProps {
actionPredicate: (id: string) => boolean;
input: EmbeddableComponentProps;
theme: ThemeServiceStart;
extraActions?: Action[];
withDefaultActions?: boolean;
}
const EmbeddablePanelWrapper: FC<EmbeddablePanelWrapperProps> = ({
@ -107,6 +114,8 @@ const EmbeddablePanelWrapper: FC<EmbeddablePanelWrapperProps> = ({
inspector,
input,
theme,
extraActions,
withDefaultActions,
}) => {
useEffect(() => {
embeddable.updateInput(input);
@ -116,7 +125,13 @@ const EmbeddablePanelWrapper: FC<EmbeddablePanelWrapperProps> = ({
<EmbeddablePanel
hideHeader={false}
embeddable={embeddable as IEmbeddable<EmbeddableInput, EmbeddableOutput>}
getActions={uiActions.getTriggerCompatibleActions}
getActions={async (triggerId, context) => {
const actions = withDefaultActions
? await uiActions.getTriggerCompatibleActions(triggerId, context)
: [];
return [...(extraActions ?? []), ...actions];
}}
inspector={inspector}
actionPredicate={actionPredicate}
showShadow={false}

View file

@ -783,7 +783,7 @@ export class LensAttributes {
};
}
getJSON(refresh?: number): TypedLensByValueInput['attributes'] {
getJSON(): TypedLensByValueInput['attributes'] {
const uniqueIndexPatternsIds = Array.from(
new Set([...this.layerConfigs.map(({ indexPattern }) => indexPattern.id)])
);
@ -792,7 +792,7 @@ export class LensAttributes {
return {
title: 'Prefilled from exploratory view app',
description: String(refresh),
description: '',
visualizationType: 'lnsXY',
references: [
...uniqueIndexPatternsIds.map((patternId) => ({

View file

@ -5,7 +5,7 @@
* 2.0.
*/
export const sampleAttribute = {
description: 'undefined',
description: '',
references: [
{
id: 'apm-*',

View file

@ -5,7 +5,7 @@
* 2.0.
*/
export const sampleAttributeCoreWebVital = {
description: 'undefined',
description: '',
references: [
{
id: 'apm-*',

View file

@ -5,7 +5,7 @@
* 2.0.
*/
export const sampleAttributeKpi = {
description: 'undefined',
description: '',
references: [
{
id: 'apm-*',

View file

@ -49,15 +49,30 @@ export function convertToShortUrl(series: SeriesUrl) {
};
}
export function createExploratoryViewRoutePath({
reportType,
allSeries,
}: {
reportType: ReportViewType;
allSeries: AllSeries;
}) {
const allShortSeries: AllShortSeries = allSeries.map((series) => convertToShortUrl(series));
return `/exploratory-view/#?reportType=${reportType}&sr=${rison.encode(
allShortSeries as unknown as RisonValue
)}`;
}
export function createExploratoryViewUrl(
{ reportType, allSeries }: { reportType: ReportViewType; allSeries: AllSeries },
baseHref = ''
baseHref = '',
appId = 'observability'
) {
const allShortSeries: AllShortSeries = allSeries.map((series) => convertToShortUrl(series));
return (
baseHref +
`/app/observability/exploratory-view/#?reportType=${reportType}&sr=${rison.encode(
`/app/${appId}/exploratory-view/#?reportType=${reportType}&sr=${rison.encode(
allShortSeries as unknown as RisonValue
)}`
);

View file

@ -12,11 +12,13 @@ import { AllSeries, useTheme } from '../../../..';
import { LayerConfig, LensAttributes } from '../configurations/lens_attributes';
import { AppDataType, ReportViewType } from '../types';
import { getLayerConfigs } from '../hooks/use_lens_attributes';
import { LensPublicStart, XYState } from '../../../../../../lens/public';
import { LensEmbeddableInput, LensPublicStart, XYState } from '../../../../../../lens/public';
import { OperationTypeComponent } from '../series_editor/columns/operation_type_select';
import { IndexPatternState } from '../hooks/use_app_index_pattern';
import { ReportConfigMap } from '../contexts/exploratory_view_config';
import { obsvReportConfigMap } from '../obsv_exploratory_view';
import { ActionTypes, useActions } from './use_actions';
import { AddToCaseAction } from '../header/add_to_case_action';
export interface ExploratoryEmbeddableProps {
reportType: ReportViewType;
@ -28,6 +30,8 @@ export interface ExploratoryEmbeddableProps {
legendIsVisible?: boolean;
dataTypesIndexPatterns?: Partial<Record<AppDataType, string>>;
reportConfigMap?: ReportConfigMap;
withActions?: boolean | ActionTypes[];
appId?: 'security' | 'observability';
}
export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps {
@ -43,17 +47,31 @@ export default function Embeddable({
appendTitle,
indexPatterns,
lens,
appId,
axisTitlesVisibility,
legendIsVisible,
withActions = true,
reportConfigMap = {},
showCalculationMethod = false,
}: ExploratoryEmbeddableComponentProps) {
const LensComponent = lens?.EmbeddableComponent;
const LensSaveModalComponent = lens?.SaveModalComponent;
const [isSaveOpen, setIsSaveOpen] = useState(false);
const [isAddToCaseOpen, setAddToCaseOpen] = useState(false);
const series = Object.entries(attributes)[0][1];
const [operationType, setOperationType] = useState(series?.operationType);
const theme = useTheme();
const actions = useActions({
withActions,
attributes,
reportType,
appId,
setIsSaveOpen,
setAddToCaseOpen,
});
const layerConfigs: LayerConfig[] = getLayerConfigs(
attributes,
@ -107,6 +125,24 @@ export default function Embeddable({
timeRange={series?.time}
attributes={attributesJSON}
onBrushEnd={({ range }) => {}}
withDefaultActions={Boolean(withActions)}
extraActions={actions}
/>
{isSaveOpen && attributesJSON && (
<LensSaveModalComponent
initialInput={attributesJSON as unknown as LensEmbeddableInput}
onClose={() => setIsSaveOpen(false)}
// if we want to do anything after the viz is saved
// right now there is no action, so an empty function
onSave={() => {}}
/>
)}
<AddToCaseAction
lensAttributes={attributesJSON}
timeRange={series?.time}
autoOpen={isAddToCaseOpen}
setAutoOpen={setAddToCaseOpen}
appId={appId}
/>
</Wrapper>
);
@ -118,5 +154,23 @@ const Wrapper = styled.div`
> :nth-child(2) {
height: calc(100% - 32px);
}
.embPanel--editing {
border-style: initial !important;
:hover {
box-shadow: none;
}
}
.embPanel__title {
display: none;
}
.embPanel__optionsMenuPopover {
visibility: collapse;
}
&&&:hover {
.embPanel__optionsMenuPopover {
visibility: visible;
}
}
}
`;

View file

@ -0,0 +1,150 @@
/*
* 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 { useCallback, useEffect, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { createExploratoryViewRoutePath, createExploratoryViewUrl } from '../configurations/utils';
import { ReportViewType } from '../types';
import { AllSeries } from '../hooks/use_series_storage';
import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
import {
Action,
ActionExecutionContext,
} from '../../../../../../../../src/plugins/ui_actions/public';
export type ActionTypes = 'explore' | 'save' | 'addToCase';
export function useActions({
withActions,
attributes,
reportType,
setIsSaveOpen,
setAddToCaseOpen,
appId = 'observability',
}: {
withActions?: boolean | ActionTypes[];
reportType: ReportViewType;
attributes: AllSeries;
appId?: 'security' | 'observability';
setIsSaveOpen: (val: boolean) => void;
setAddToCaseOpen: (val: boolean) => void;
}) {
const [defaultActions, setDefaultActions] = useState(['explore', 'save', 'addToCase']);
useEffect(() => {
if (withActions === false) {
setDefaultActions([]);
}
if (Array.isArray(withActions)) {
setDefaultActions(withActions);
}
}, [withActions]);
const { http, application } = useKibana().services;
const href = createExploratoryViewUrl(
{ reportType, allSeries: attributes },
http?.basePath.get(),
appId
);
const routePath = createExploratoryViewRoutePath({ reportType, allSeries: attributes });
const exploreCallback = useCallback(() => {
application?.navigateToApp(appId, { path: routePath });
}, [appId, application, routePath]);
const saveCallback = useCallback(() => {
setIsSaveOpen(true);
}, [setIsSaveOpen]);
const addToCaseCallback = useCallback(() => {
setAddToCaseOpen(true);
}, [setAddToCaseOpen]);
return defaultActions.map<Action>((action) => {
if (action === 'save') {
return getSaveAction({ callback: saveCallback });
}
if (action === 'addToCase') {
return getAddToCaseAction({ callback: addToCaseCallback });
}
return getExploreAction({ href, callback: exploreCallback });
});
}
const getExploreAction = ({ href, callback }: { href: string; callback: () => void }): Action => {
return {
id: 'expViewExplore',
getDisplayName(context: ActionExecutionContext<object>): string {
return i18n.translate('xpack.observability.expView.explore', {
defaultMessage: 'Explore',
});
},
getIconType(context: ActionExecutionContext<object>): string | undefined {
return 'visArea';
},
type: 'link',
async isCompatible(context: ActionExecutionContext<object>): Promise<boolean> {
return true;
},
async getHref(context: ActionExecutionContext<object>): Promise<string | undefined> {
return href;
},
async execute(context: ActionExecutionContext<object>): Promise<void> {
callback();
return;
},
order: 50,
};
};
const getSaveAction = ({ callback }: { callback: () => void }): Action => {
return {
id: 'expViewSave',
getDisplayName(context: ActionExecutionContext<object>): string {
return i18n.translate('xpack.observability.expView.save', {
defaultMessage: 'Save visualization',
});
},
getIconType(context: ActionExecutionContext<object>): string | undefined {
return 'save';
},
type: 'actionButton',
async isCompatible(context: ActionExecutionContext<object>): Promise<boolean> {
return true;
},
async execute(context: ActionExecutionContext<object>): Promise<void> {
callback();
return;
},
order: 49,
};
};
const getAddToCaseAction = ({ callback }: { callback: () => void }): Action => {
return {
id: 'expViewAddToCase',
getDisplayName(context: ActionExecutionContext<object>): string {
return i18n.translate('xpack.observability.expView.addToCase', {
defaultMessage: 'Add to case',
});
},
getIconType(context: ActionExecutionContext<object>): string | undefined {
return 'link';
},
type: 'actionButton',
async isCompatible(context: ActionExecutionContext<object>): Promise<boolean> {
return true;
},
async execute(context: ActionExecutionContext<object>): Promise<void> {
callback();
return;
},
order: 48,
};
};

View file

@ -7,7 +7,7 @@
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiLink } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback } from 'react';
import React, { useCallback, useEffect } from 'react';
import { toMountPoint, useKibana } from '../../../../../../../../src/plugins/kibana_react/public';
import { ObservabilityAppServices } from '../../../../application/types';
import {
@ -21,11 +21,20 @@ import { observabilityFeatureId, observabilityAppId } from '../../../../../commo
import { parseRelativeDate } from '../components/date_range_picker';
export interface AddToCaseProps {
autoOpen?: boolean;
setAutoOpen?: (val: boolean) => void;
timeRange: { from: string; to: string };
appId?: 'security' | 'observability';
lensAttributes: TypedLensByValueInput['attributes'] | null;
}
export function AddToCaseAction({ lensAttributes, timeRange }: AddToCaseProps) {
export function AddToCaseAction({
lensAttributes,
timeRange,
autoOpen,
setAutoOpen,
appId,
}: AddToCaseProps) {
const kServices = useKibana<ObservabilityAppServices>().services;
const {
@ -58,6 +67,7 @@ export function AddToCaseAction({ lensAttributes, timeRange }: AddToCaseProps) {
from: absoluteFromDate?.toISOString() ?? '',
to: absoluteToDate?.toISOString() ?? '',
},
appId,
});
const getAllCasesSelectorModalProps: GetAllCasesSelectorModalProps = {
@ -69,22 +79,36 @@ export function AddToCaseAction({ lensAttributes, timeRange }: AddToCaseProps) {
},
};
useEffect(() => {
if (autoOpen) {
setIsCasesOpen(true);
}
}, [autoOpen, setIsCasesOpen]);
useEffect(() => {
if (!isCasesOpen) {
setAutoOpen?.(false);
}
}, [isCasesOpen, setAutoOpen]);
return (
<>
<EuiButtonEmpty
size="s"
isLoading={isSaving}
isDisabled={lensAttributes === null}
onClick={() => {
if (lensAttributes) {
setIsCasesOpen(true);
}
}}
>
{i18n.translate('xpack.observability.expView.heading.addToCase', {
defaultMessage: 'Add to case',
})}
</EuiButtonEmpty>
{typeof autoOpen === 'undefined' && (
<EuiButtonEmpty
size="s"
isLoading={isSaving}
isDisabled={lensAttributes === null}
onClick={() => {
if (lensAttributes) {
setIsCasesOpen(true);
}
}}
>
{i18n.translate('xpack.observability.expView.heading.addToCase', {
defaultMessage: 'Add to case',
})}
</EuiButtonEmpty>
)}
{isCasesOpen &&
lensAttributes &&
cases.getAllCasesSelectorModal(getAllCasesSelectorModalProps)}

View file

@ -41,7 +41,11 @@ export const useAddToCase = ({
lensAttributes,
getToastText,
timeRange,
}: AddToCaseProps & { getToastText: (thaCase: Case | SubCase) => MountPoint<HTMLElement> }) => {
appId,
}: AddToCaseProps & {
appId?: 'security' | 'observability';
getToastText: (thaCase: Case | SubCase) => MountPoint<HTMLElement>;
}) => {
const [isSaving, setIsSaving] = useState(false);
const [isCasesOpen, setIsCasesOpen] = useState(false);
@ -87,13 +91,13 @@ export const useAddToCase = ({
}
);
} else {
navigateToApp(observabilityFeatureId, {
navigateToApp(appId || observabilityFeatureId, {
deepLinkId: CasesDeepLinkId.casesCreate,
openInNewTab: true,
});
}
},
[getToastText, http, lensAttributes, navigateToApp, timeRange, toasts]
[appId, getToastText, http, lensAttributes, navigateToApp, timeRange, toasts]
);
return {

View file

@ -118,7 +118,7 @@ export const useLensAttributes = (): TypedLensByValueInput['attributes'] | null
const lensAttributes = new LensAttributes(layerConfigs);
return lensAttributes.getJSON(lastRefresh);
return lensAttributes.getJSON();
// we also want to check the state on allSeries changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [indexPatterns, reportType, storage, theme, lastRefresh, allSeries]);

View file

@ -6,9 +6,9 @@
*/
import { i18n } from '@kbn/i18n';
import React, { Dispatch, SetStateAction, useCallback } from 'react';
import React, { Dispatch, SetStateAction, useCallback, useState } from 'react';
import styled from 'styled-components';
import { TypedLensByValueInput } from '../../../../../lens/public';
import { LensEmbeddableInput, TypedLensByValueInput } from '../../../../../lens/public';
import { useUiTracker } from '../../../hooks/use_track_metric';
import { useSeriesStorage } from './hooks/use_series_storage';
import { ObservabilityPublicPluginsStart } from '../../../plugin';
@ -30,9 +30,12 @@ export function LensEmbeddable(props: Props) {
} = useKibana<ObservabilityPublicPluginsStart>();
const LensComponent = lens?.EmbeddableComponent;
const LensSaveModalComponent = lens?.SaveModalComponent;
const { firstSeries, setSeries, reportType, lastRefresh } = useSeriesStorage();
const [isSaveOpen, setIsSaveOpen] = useState(false);
const firstSeriesId = 0;
const timeRange = useExpViewTimeRange();
@ -90,6 +93,15 @@ export function LensEmbeddable(props: Props) {
onLoad={onLensLoad}
onBrushEnd={onBrushEnd}
/>
{isSaveOpen && lensAttributes && (
<LensSaveModalComponent
initialInput={lensAttributes as unknown as LensEmbeddableInput}
onClose={() => setIsSaveOpen(false)}
// if we want to do anything after the viz is saved
// right now there is no action, so an empty function
onSave={() => {}}
/>
)}
</LensWrapper>
);
}
@ -97,6 +109,26 @@ export function LensEmbeddable(props: Props) {
const LensWrapper = styled.div`
height: 100%;
.embPanel__optionsMenuPopover {
visibility: collapse;
}
&&&:hover {
.embPanel__optionsMenuPopover {
visibility: visible;
}
}
&& .embPanel--editing {
border-style: initial !important;
:hover {
box-shadow: none;
}
}
.embPanel__title {
display: none;
}
&&& > div {
height: 100%;
}

View file

@ -5,6 +5,7 @@
"optionalPlugins": ["cloud", "data", "fleet", "home", "ml"],
"requiredPlugins": [
"alerting",
"cases",
"embeddable",
"encryptedSavedObjects",
"features",

View file

@ -48,6 +48,7 @@ import {
import { LazySyntheticsCustomAssetsExtension } from '../components/fleet_package/lazy_synthetics_custom_assets_extension';
import { Start as InspectorPluginStart } from '../../../../../src/plugins/inspector/public';
import { UptimeUiConfig } from '../../common/config';
import { CasesUiStart } from '../../../cases/public';
export interface ClientPluginsSetup {
home?: HomePublicPluginSetup;
@ -65,6 +66,7 @@ export interface ClientPluginsStart {
observability: ObservabilityPublicStart;
share: SharePluginStart;
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
cases: CasesUiStart;
}
export interface UptimePluginServices extends Partial<CoreStart> {

View file

@ -120,6 +120,7 @@ const Application = (props: UptimeAppProps) => {
inspector: startPlugins.inspector,
triggersActionsUi: startPlugins.triggersActionsUi,
observability: startPlugins.observability,
cases: startPlugins.cases,
}}
>
<Router history={appMountParameters.history}>

View file

@ -88,6 +88,7 @@ export function StepFieldTrend({
}
: undefined
}
withActions={false}
/>
</Wrapper>
);