From 49b5ef86d938291bf1919437feeae0995226ad50 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Tue, 25 Jan 2022 13:29:49 +0100 Subject: [PATCH 1/9] add lens extra actions --- .../public/lib/panel/embeddable_panel.tsx | 2 + .../exploratory_view_example/kibana.json | 7 +- .../exploratory_view_example/public/app.tsx | 1 + .../exploratory_view_example/public/mount.tsx | 17 +- .../embeddable/embeddable_component.tsx | 23 ++- .../exploratory_view/configurations/utils.ts | 12 +- .../embeddable/embeddable.tsx | 46 +++++- .../embeddable/use_actions.ts | 152 ++++++++++++++++++ .../header/add_to_case_action.tsx | 56 +++++-- .../exploratory_view/hooks/use_add_to_case.ts | 10 +- .../exploratory_view/lens_embeddable.tsx | 36 ++++- x-pack/plugins/uptime/kibana.json | 1 + x-pack/plugins/uptime/public/apps/plugin.ts | 2 + .../plugins/uptime/public/apps/uptime_app.tsx | 1 + 14 files changed, 328 insertions(+), 38 deletions(-) create mode 100644 x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index 2e501984dfa76..21e052b5dfb62 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -80,6 +80,7 @@ interface Props { actionPredicate?: (actionId: string) => boolean; reportUiCounter?: UsageCollectionStart['reportUiCounter']; showShadow?: boolean; + hasBorder?: boolean; showBadges?: boolean; showNotifications?: boolean; containerContext?: EmbeddableContainerContext; @@ -273,6 +274,7 @@ export class EmbeddablePanel extends React.Component { role="figure" aria-labelledby={headerId} hasShadow={this.props.showShadow} + hasBorder={false} > {!this.props.hideHeader && ( 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..d6051ee5fb59c 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,7 @@ export type TypedLensByValueInput = Omit & { }; export type EmbeddableComponentProps = (TypedLensByValueInput | LensByReferenceInput) & { - withActions?: boolean; + withActions?: boolean | Action[]; }; interface PluginsStartDependencies { @@ -67,7 +67,7 @@ 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.withActions); const theme = core.theme; if (loading) { @@ -83,6 +83,7 @@ export function getEmbeddableComponent(core: CoreStart, plugins: PluginsStartDep actionPredicate={() => hasActions} input={input} theme={theme} + extraActions={Array.isArray(props.withActions) ? props.withActions : []} /> ); } @@ -98,6 +99,9 @@ interface EmbeddablePanelWrapperProps { actionPredicate: (id: string) => boolean; input: EmbeddableComponentProps; theme: ThemeServiceStart; + hideHeader?: boolean; + showShadow?: boolean; + extraActions: Action[]; } const EmbeddablePanelWrapper: FC = ({ @@ -107,6 +111,9 @@ const EmbeddablePanelWrapper: FC = ({ inspector, input, theme, + extraActions, + hideHeader = false, + showShadow = false, }) => { useEffect(() => { embeddable.updateInput(input); @@ -114,15 +121,19 @@ const EmbeddablePanelWrapper: FC = ({ return ( } - getActions={uiActions.getTriggerCompatibleActions} + getActions={async (triggerId, context) => { + const actions = await uiActions.getTriggerCompatibleActions(triggerId, context); + return [...extraActions, ...actions]; + }} inspector={inspector} actionPredicate={actionPredicate} - showShadow={false} + showShadow={showShadow} showBadges={false} showNotifications={false} theme={theme} + hasBorder={false} /> ); }; 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..e504cbb1568ae 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 @@ -51,13 +51,21 @@ export function convertToShortUrl(series: SeriesUrl) { export function createExploratoryViewUrl( { reportType, allSeries }: { reportType: ReportViewType; allSeries: AllSeries }, - baseHref = '' + baseHref = '', + appId = 'observability', + onlyPath?: boolean ) { const allShortSeries: AllShortSeries = allSeries.map((series) => convertToShortUrl(series)); + if (onlyPath) { + return `/exploratory-view/#?reportType=${reportType}&sr=${rison.encode( + allShortSeries as unknown as RisonValue + )}`; + } + 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..c106cd6210975 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,23 @@ export default function Embeddable({ timeRange={series?.time} attributes={attributesJSON} onBrushEnd={({ range }) => {}} + withActions={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 +153,14 @@ 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; + } } `; 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..2b667747ef5c1 --- /dev/null +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/use_actions.ts @@ -0,0 +1,152 @@ +/* + * 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 { 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 hrefPath = createExploratoryViewUrl( + { reportType, allSeries: attributes }, + http?.basePath.get(), + appId, + true + ); + + const exploreCallback = useCallback(() => { + application?.navigateToApp(appId, { path: hrefPath }); + }, [appId, application, hrefPath]); + + 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; + }, + }; +}; + +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; + }, + }; +}; + +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; + }, + }; +}; 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/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, }} > From 544a3c16523597156a2b3771a9e794adfa529277 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Tue, 25 Jan 2022 14:43:38 +0100 Subject: [PATCH 2/9] add extra actions --- src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx | 2 -- x-pack/plugins/lens/public/embeddable/embeddable_component.tsx | 1 - .../shared/exploratory_view/embeddable/embeddable.tsx | 2 +- .../shared/exploratory_view/embeddable/use_actions.ts | 3 +++ .../components/synthetics/check_steps/step_field_trend.tsx | 1 + 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index 21e052b5dfb62..2e501984dfa76 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -80,7 +80,6 @@ interface Props { actionPredicate?: (actionId: string) => boolean; reportUiCounter?: UsageCollectionStart['reportUiCounter']; showShadow?: boolean; - hasBorder?: boolean; showBadges?: boolean; showNotifications?: boolean; containerContext?: EmbeddableContainerContext; @@ -274,7 +273,6 @@ export class EmbeddablePanel extends React.Component { role="figure" aria-labelledby={headerId} hasShadow={this.props.showShadow} - hasBorder={false} > {!this.props.hideHeader && ( = ({ showBadges={false} showNotifications={false} theme={theme} - hasBorder={false} /> ); }; 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 c106cd6210975..5eec0f453c761 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 @@ -125,7 +125,7 @@ export default function Embeddable({ timeRange={series?.time} attributes={attributesJSON} onBrushEnd={({ range }) => {}} - withActions={actions} + withActions={withActions ? actions : false} /> {isSaveOpen && attributesJSON && ( vo callback(); return; }, + order: 50, }; }; @@ -126,6 +127,7 @@ const getSaveAction = ({ callback }: { callback: () => void }): Action => { callback(); return; }, + order: 49, }; }; @@ -148,5 +150,6 @@ const getAddToCaseAction = ({ callback }: { callback: () => void }): Action => { callback(); return; }, + order: 48, }; }; 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} /> ); From 96ed1afcaa9ba107af091e0da39645bf6bd32b36 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Tue, 25 Jan 2022 14:47:50 +0100 Subject: [PATCH 3/9] nit --- .../lens/public/embeddable/embeddable_component.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index b102044154261..297dfbaa59d8f 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -99,8 +99,6 @@ interface EmbeddablePanelWrapperProps { actionPredicate: (id: string) => boolean; input: EmbeddableComponentProps; theme: ThemeServiceStart; - hideHeader?: boolean; - showShadow?: boolean; extraActions: Action[]; } @@ -112,8 +110,6 @@ const EmbeddablePanelWrapper: FC = ({ input, theme, extraActions, - hideHeader = false, - showShadow = false, }) => { useEffect(() => { embeddable.updateInput(input); @@ -121,7 +117,6 @@ const EmbeddablePanelWrapper: FC = ({ return ( } getActions={async (triggerId, context) => { const actions = await uiActions.getTriggerCompatibleActions(triggerId, context); @@ -129,7 +124,7 @@ const EmbeddablePanelWrapper: FC = ({ }} inspector={inspector} actionPredicate={actionPredicate} - showShadow={showShadow} + showShadow={false} showBadges={false} showNotifications={false} theme={theme} From ef94273648c37f0529c58b34a1bc64738bb10fc7 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Tue, 25 Jan 2022 14:50:26 +0100 Subject: [PATCH 4/9] nit --- x-pack/plugins/lens/public/embeddable/embeddable_component.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx index 297dfbaa59d8f..db979e113f472 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable_component.tsx @@ -117,6 +117,7 @@ const EmbeddablePanelWrapper: FC = ({ return ( } getActions={async (triggerId, context) => { const actions = await uiActions.getTriggerCompatibleActions(triggerId, context); From 12d4b73d28704b444dee254b40ea5361090fac35 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Tue, 25 Jan 2022 14:54:46 +0100 Subject: [PATCH 5/9] update style --- .../shared/exploratory_view/embeddable/embeddable.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) 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 5eec0f453c761..4913a6d16f97e 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 @@ -162,5 +162,14 @@ const Wrapper = styled.div` .embPanel__title { display: none; } + .embPanel__optionsMenuPopover { + visibility: collapse; + } + + &&&:hover { + .embPanel__optionsMenuPopover { + visibility: visible; + } + } } `; From f2b0e051681014a3e3c341335da5b23d23612508 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Thu, 27 Jan 2022 13:54:29 +0100 Subject: [PATCH 6/9] update props --- .../embedded_lens_example/public/app.tsx | 53 ++++++++++++++++++- .../embeddable/embeddable_component.tsx | 21 +++++--- .../configurations/lens_attributes.ts | 4 +- .../embeddable/embeddable.tsx | 3 +- 4 files changed, 70 insertions(+), 11 deletions(-) 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 && ( & { }; export type EmbeddableComponentProps = (TypedLensByValueInput | LensByReferenceInput) & { - withActions?: boolean | Action[]; + 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 = Boolean(props.withActions); + const hasActions = + Boolean(props.withDefaultActions) || (props.extraActions && props.extraActions?.length > 0); + const theme = core.theme; if (loading) { @@ -83,7 +86,8 @@ export function getEmbeddableComponent(core: CoreStart, plugins: PluginsStartDep actionPredicate={() => hasActions} input={input} theme={theme} - extraActions={Array.isArray(props.withActions) ? props.withActions : []} + extraActions={props.extraActions} + withDefaultActions={props.withDefaultActions} /> ); } @@ -99,7 +103,8 @@ interface EmbeddablePanelWrapperProps { actionPredicate: (id: string) => boolean; input: EmbeddableComponentProps; theme: ThemeServiceStart; - extraActions: Action[]; + extraActions?: Action[]; + withDefaultActions?: boolean; } const EmbeddablePanelWrapper: FC = ({ @@ -110,6 +115,7 @@ const EmbeddablePanelWrapper: FC = ({ input, theme, extraActions, + withDefaultActions, }) => { useEffect(() => { embeddable.updateInput(input); @@ -120,8 +126,11 @@ const EmbeddablePanelWrapper: FC = ({ hideHeader={false} embeddable={embeddable as IEmbeddable} getActions={async (triggerId, context) => { - const actions = await uiActions.getTriggerCompatibleActions(triggerId, context); - return [...extraActions, ...actions]; + const actions = withDefaultActions + ? await uiActions.getTriggerCompatibleActions(triggerId, context) + : []; + + return [...(extraActions ?? []), ...actions]; }} inspector={inspector} actionPredicate={actionPredicate} 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/embeddable/embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx index 4913a6d16f97e..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 @@ -125,7 +125,8 @@ export default function Embeddable({ timeRange={series?.time} attributes={attributesJSON} onBrushEnd={({ range }) => {}} - withActions={withActions ? actions : false} + withDefaultActions={Boolean(withActions)} + extraActions={actions} /> {isSaveOpen && attributesJSON && ( Date: Thu, 27 Jan 2022 14:52:08 +0100 Subject: [PATCH 7/9] PR feedback --- .../exploratory_view/configurations/utils.ts | 23 ++++++++++++------- .../embeddable/use_actions.ts | 13 ++++------- 2 files changed, 19 insertions(+), 17 deletions(-) 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 e504cbb1568ae..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,20 +49,27 @@ 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 = '', - appId = 'observability', - onlyPath?: boolean + appId = 'observability' ) { const allShortSeries: AllShortSeries = allSeries.map((series) => convertToShortUrl(series)); - if (onlyPath) { - return `/exploratory-view/#?reportType=${reportType}&sr=${rison.encode( - allShortSeries as unknown as RisonValue - )}`; - } - return ( baseHref + `/app/${appId}/exploratory-view/#?reportType=${reportType}&sr=${rison.encode( 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 index 11ae0ea8e15cc..5c5c22ddf7c53 100644 --- 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 @@ -7,7 +7,7 @@ import { useCallback, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { createExploratoryViewUrl } from '../configurations/utils'; +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'; @@ -52,16 +52,11 @@ export function useActions({ appId ); - const hrefPath = createExploratoryViewUrl( - { reportType, allSeries: attributes }, - http?.basePath.get(), - appId, - true - ); + const routePath = createExploratoryViewRoutePath({ reportType, allSeries: attributes }); const exploreCallback = useCallback(() => { - application?.navigateToApp(appId, { path: hrefPath }); - }, [appId, application, hrefPath]); + application?.navigateToApp(appId, { path: routePath }); + }, [appId, application, routePath]); const saveCallback = useCallback(() => { setIsSaveOpen(true); From b3e2a91db678d41793fdabd63f0c522e845c6145 Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Thu, 27 Jan 2022 14:53:18 +0100 Subject: [PATCH 8/9] update type --- .../shared/exploratory_view/hooks/use_lens_attributes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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]); From aa63cec1d1e30331559451b423765f21441180bf Mon Sep 17 00:00:00 2001 From: shahzad31 Date: Thu, 27 Jan 2022 15:59:24 +0100 Subject: [PATCH 9/9] update test --- .../configurations/test_data/sample_attribute.ts | 2 +- .../configurations/test_data/sample_attribute_cwv.ts | 2 +- .../configurations/test_data/sample_attribute_kpi.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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-*',