diff --git a/x-pack/examples/embedded_lens_example/public/app.tsx b/x-pack/examples/embedded_lens_example/public/app.tsx index 510d9469c7878..2e2e973e7cc6b 100644 --- a/x-pack/examples/embedded_lens_example/public/app.tsx +++ b/x-pack/examples/embedded_lens_example/public/app.tsx @@ -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.

- + + + { + setEnableExtraAction((prevState) => !prevState); + }} + > + {enableExtraAction ? 'Disable extra action' : 'Enable extra action'} + + + + { + setEnableDefaultAction((prevState) => !prevState); + }} + > + {enableDefaultAction ? 'Disable default action' : 'Enable default action'} + + 'save', + async isCompatible( + context: ActionExecutionContext + ): Promise { + return true; + }, + execute: async (context: ActionExecutionContext) => { + alert('I am an extra action'); + return; + }, + getDisplayName: () => 'Extra action', + }, + ] + : undefined + } /> {isSaveModalVisible && ( diff --git a/x-pack/examples/exploratory_view_example/public/mount.tsx b/x-pack/examples/exploratory_view_example/public/mount.tsx index 58ec363223270..b589db9d531b7 100644 --- a/x-pack/examples/exploratory_view_example/public/mount.tsx +++ b/x-pack/examples/exploratory_view_example/public/mount.tsx @@ -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) => async ({ element }: AppMountParameters) => { @@ -26,9 +29,13 @@ export const mount = const i18nCore = core.i18n; const reactElement = ( - - - + + + + + + + ); render(reactElement, element); return () => unmountComponentAtNode(element); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index e501138648b14..94473f2be3613 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -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 & { }; 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 = ({ @@ -107,6 +114,8 @@ const EmbeddablePanelWrapper: FC = ({ inspector, input, theme, + extraActions, + withDefaultActions, }) => { useEffect(() => { embeddable.updateInput(input); @@ -116,7 +125,13 @@ const EmbeddablePanelWrapper: FC = ({ } - getActions={uiActions.getTriggerCompatibleActions} + getActions={async (triggerId, context) => { + const actions = withDefaultActions + ? await uiActions.getTriggerCompatibleActions(triggerId, context) + : []; + + return [...(extraActions ?? []), ...actions]; + }} inspector={inspector} actionPredicate={actionPredicate} showShadow={false} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index f873b1eb5cbab..1058973a4432d 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -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) => ({ diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute.ts index 1a9c87fc826bd..ba4b4a3bce4d9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute.ts @@ -5,7 +5,7 @@ * 2.0. */ export const sampleAttribute = { - description: 'undefined', + description: '', references: [ { id: 'apm-*', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts index 5f373de200897..f595e7a147812 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_cwv.ts @@ -5,7 +5,7 @@ * 2.0. */ export const sampleAttributeCoreWebVital = { - description: 'undefined', + description: '', references: [ { id: 'apm-*', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_kpi.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_kpi.ts index 415b2eb0d4c7a..52ccdc2918e4c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_kpi.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/test_data/sample_attribute_kpi.ts @@ -5,7 +5,7 @@ * 2.0. */ export const sampleAttributeKpi = { - description: 'undefined', + description: '', references: [ { id: 'apm-*', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts index 32d428916501c..29f751258e02d 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/utils.ts @@ -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 )}` ); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx index 8aa76d0e6228a..92ef0834880d5 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -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>; 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 && ( + setIsSaveOpen(false)} + // if we want to do anything after the viz is saved + // right now there is no action, so an empty function + onSave={() => {}} + /> + )} + ); @@ -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; + } + } } `; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts new file mode 100644 index 0000000000000..5c5c22ddf7c53 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts @@ -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) => { + 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): string { + return i18n.translate('xpack.observability.expView.explore', { + defaultMessage: 'Explore', + }); + }, + getIconType(context: ActionExecutionContext): string | undefined { + return 'visArea'; + }, + type: 'link', + async isCompatible(context: ActionExecutionContext): Promise { + return true; + }, + async getHref(context: ActionExecutionContext): Promise { + return href; + }, + async execute(context: ActionExecutionContext): Promise { + callback(); + return; + }, + order: 50, + }; +}; + +const getSaveAction = ({ callback }: { callback: () => void }): Action => { + return { + id: 'expViewSave', + getDisplayName(context: ActionExecutionContext): string { + return i18n.translate('xpack.observability.expView.save', { + defaultMessage: 'Save visualization', + }); + }, + getIconType(context: ActionExecutionContext): string | undefined { + return 'save'; + }, + type: 'actionButton', + async isCompatible(context: ActionExecutionContext): Promise { + return true; + }, + async execute(context: ActionExecutionContext): Promise { + callback(); + return; + }, + order: 49, + }; +}; + +const getAddToCaseAction = ({ callback }: { callback: () => void }): Action => { + return { + id: 'expViewAddToCase', + getDisplayName(context: ActionExecutionContext): string { + return i18n.translate('xpack.observability.expView.addToCase', { + defaultMessage: 'Add to case', + }); + }, + getIconType(context: ActionExecutionContext): string | undefined { + return 'link'; + }, + type: 'actionButton', + async isCompatible(context: ActionExecutionContext): Promise { + return true; + }, + async execute(context: ActionExecutionContext): Promise { + callback(); + return; + }, + order: 48, + }; +}; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx index f1607bc49a384..50f44f2d89b9b 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/header/add_to_case_action.tsx @@ -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().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 ( <> - { - if (lensAttributes) { - setIsCasesOpen(true); - } - }} - > - {i18n.translate('xpack.observability.expView.heading.addToCase', { - defaultMessage: 'Add to case', - })} - + {typeof autoOpen === 'undefined' && ( + { + if (lensAttributes) { + setIsCasesOpen(true); + } + }} + > + {i18n.translate('xpack.observability.expView.heading.addToCase', { + defaultMessage: 'Add to case', + })} + + )} {isCasesOpen && lensAttributes && cases.getAllCasesSelectorModal(getAllCasesSelectorModalProps)} diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts index 1f6620e632eff..2511fe44bfbc3 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_add_to_case.ts @@ -41,7 +41,11 @@ export const useAddToCase = ({ lensAttributes, getToastText, timeRange, -}: AddToCaseProps & { getToastText: (thaCase: Case | SubCase) => MountPoint }) => { + appId, +}: AddToCaseProps & { + appId?: 'security' | 'observability'; + getToastText: (thaCase: Case | SubCase) => MountPoint; +}) => { 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 { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts index cc258af65973e..aa507ded8e20a 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_lens_attributes.ts @@ -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]); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx index f7f63097e2926..d5cd5379daca0 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/lens_embeddable.tsx @@ -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(); 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 && ( + setIsSaveOpen(false)} + // if we want to do anything after the viz is saved + // right now there is no action, so an empty function + onSave={() => {}} + /> + )} ); } @@ -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%; } diff --git a/x-pack/plugins/uptime/kibana.json b/x-pack/plugins/uptime/kibana.json index 35be0b19d4521..461358c27fe3b 100644 --- a/x-pack/plugins/uptime/kibana.json +++ b/x-pack/plugins/uptime/kibana.json @@ -5,6 +5,7 @@ "optionalPlugins": ["cloud", "data", "fleet", "home", "ml"], "requiredPlugins": [ "alerting", + "cases", "embeddable", "encryptedSavedObjects", "features", diff --git a/x-pack/plugins/uptime/public/apps/plugin.ts b/x-pack/plugins/uptime/public/apps/plugin.ts index dd2287b3b1642..a5e2a85953d65 100644 --- a/x-pack/plugins/uptime/public/apps/plugin.ts +++ b/x-pack/plugins/uptime/public/apps/plugin.ts @@ -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 { diff --git a/x-pack/plugins/uptime/public/apps/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx index 703b9a3d6123b..5df0d1a00f905 100644 --- a/x-pack/plugins/uptime/public/apps/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx @@ -120,6 +120,7 @@ const Application = (props: UptimeAppProps) => { inspector: startPlugins.inspector, triggersActionsUi: startPlugins.triggersActionsUi, observability: startPlugins.observability, + cases: startPlugins.cases, }} > diff --git a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_field_trend.tsx b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_field_trend.tsx index 8c270f4bc2199..01e0d8709905c 100644 --- a/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_field_trend.tsx +++ b/x-pack/plugins/uptime/public/components/synthetics/check_steps/step_field_trend.tsx @@ -88,6 +88,7 @@ export function StepFieldTrend({ } : undefined } + withActions={false} /> );