From 636f9c52f8a809b6e32b1af4f0307302393312de Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 11 Sep 2023 15:45:10 +0300 Subject: [PATCH 01/44] [Lens] Inline editing of lens panels --- .../public/chart/chart_config_panel.tsx | 4 +- .../lens/public/app_plugin/mounter.tsx | 2 + .../get_edit_lens_configuration.tsx | 20 +++- .../lens_configuration_flyout.tsx | 108 +++++++++++++++--- .../plugins/lens/public/app_plugin/types.ts | 2 + .../lens/public/embeddable/embeddable.tsx | 9 +- .../open_lens_config/helpers.ts | 2 +- 7 files changed, 128 insertions(+), 19 deletions(-) diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx index c3faacd5b9a40..5c5ccab277c87 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx @@ -49,9 +49,7 @@ export function ChartConfigPanel({ ...(datasourceState && { datasourceState }), ...(visualizationState && { visualizationState }), } as Suggestion; - if (!isEqual(updatedSuggestion, currentSuggestion)) { - onSuggestionChange?.(updatedSuggestion); - } + onSuggestionChange?.(updatedSuggestion); }, [currentSuggestion, onSuggestionChange] ); diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx index 3f6009659199c..351a479720453 100644 --- a/x-pack/plugins/lens/public/app_plugin/mounter.tsx +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -102,6 +102,7 @@ export async function getLensServices( share, unifiedSearch, serverless, + contentManagement, } = startDependencies; const storage = new Storage(localStorage); @@ -114,6 +115,7 @@ export async function getLensServices( storage, inspector: getLensInspectorService(inspector), navigation, + contentManagement, fieldFormats, stateTransfer, usageCollection, diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index b0e3ec119c532..8d0eb0f15298f 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { EuiFlyout, EuiLoadingSpinner, EuiOverlayMask } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Provider } from 'react-redux'; @@ -29,6 +29,8 @@ import { LensEditConfigurationFlyout, type EditConfigPanelProps, } from './lens_configuration_flyout'; +import { SavedObjectIndexStore, type Document } from '../../../persistence'; +import { DOC_TYPE } from '../../../../common/constants'; export type EditLensConfigurationProps = Omit< EditConfigPanelProps, @@ -93,10 +95,23 @@ export async function getEditLensConfiguration( datasourceId, adaptersTables, panelId, + savedObjectId, + updateByRefInput, }: EditLensConfigurationProps) => { if (!lensServices || !datasourceMap || !visualizationMap || !dataView.id) { return ; } + const saveByRef = useCallback( + async (attrs: Document) => { + const savedObjectStore = new SavedObjectIndexStore(lensServices.contentManagement.client); + await savedObjectStore.save({ + ...attrs, + savedObjectId, + type: DOC_TYPE, + }); + }, + [savedObjectId] + ); const datasourceState = attributes.state.datasourceStates[datasourceId]; const storeDeps = { lensServices, @@ -159,6 +174,9 @@ export async function getEditLensConfiguration( startDependencies, visualizationMap, datasourceMap, + saveByRef, + savedObjectId, + updateByRefInput, }; return getWrapper( diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 95cef3bba61bb..e36d7a44fc918 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -5,9 +5,10 @@ * 2.0. */ -import React, { useMemo } from 'react'; +import React, { useMemo, useCallback, useRef } from 'react'; import { EuiButtonEmpty, + EuiButton, EuiFlyoutBody, EuiFlyoutFooter, EuiSpacer, @@ -30,6 +31,8 @@ import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame import type { DatasourceMap, VisualizationMap } from '../../../types'; import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; import { ConfigPanelWrapper } from '../../../editor_frame_service/editor_frame/config_panel/config_panel'; +import { extractReferencesFromState } from '../../../utils'; +import type { Document } from '../../../persistence'; export interface EditConfigPanelProps { attributes: TypedLensByValueInput['attributes']; @@ -42,8 +45,11 @@ export interface EditConfigPanelProps { closeFlyout?: () => void; wrapInFlyout?: boolean; panelId?: string; + savedObjectId?: string; datasourceId: 'formBased' | 'textBased'; adaptersTables?: Record; + saveByRef?: (attrs: Document) => void; + updateByRefInput?: (soId: string) => void; } export function LensEditConfigurationFlyout({ @@ -57,12 +63,71 @@ export function LensEditConfigurationFlyout({ updateAll, closeFlyout, adaptersTables, + saveByRef, + savedObjectId, + updateByRefInput, }: EditConfigPanelProps) { + const previousAttributes = useRef(attributes); const datasourceState = attributes.state.datasourceStates[datasourceId]; const activeVisualization = visualizationMap[attributes.visualizationType]; const activeDatasource = datasourceMap[datasourceId]; const { euiTheme } = useEuiTheme(); + const onCancel = useCallback(() => { + const attrs = previousAttributes.current; + updateAll?.(attrs.state.datasourceStates[datasourceId], attrs.state.visualization); + if (savedObjectId) { + updateByRefInput?.(savedObjectId); + } + closeFlyout?.(); + }, [updateAll, datasourceId, savedObjectId, closeFlyout, updateByRefInput]); + const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); + + const onApply = useCallback(() => { + if (savedObjectId) { + const dsStates = Object.fromEntries( + Object.entries(datasourceStates).map(([id, ds]) => { + const dsState = ds.state; + return [id, dsState]; + }) + ); + const references = extractReferencesFromState({ + activeDatasources: Object.keys(datasourceStates).reduce( + (acc, id) => ({ + ...acc, + [id]: datasourceMap[id], + }), + {} + ), + datasourceStates, + visualizationState: visualization.state, + activeVisualization, + }); + const attrs = { + ...attributes, + state: { + ...attributes.state, + visualization: visualization.state, + datasourceStates: dsStates, + }, + references, + }; + saveByRef?.(attrs); + updateByRefInput?.(savedObjectId); + } + closeFlyout?.(); + }, [ + activeVisualization, + savedObjectId, + closeFlyout, + attributes, + datasourceMap, + visualization.state, + datasourceStates, + saveByRef, + updateByRefInput, + ]); + const activeData: Record = useMemo(() => { return {}; }, []); @@ -83,7 +148,6 @@ export function LensEditConfigurationFlyout({ }; return selectFramePublicAPI(newState, datasourceMap); }); - const { isLoading } = useLensSelector((state) => state.lens); if (isLoading) return null; const layerPanelsProps = { @@ -139,17 +203,35 @@ export function LensEditConfigurationFlyout({ - - - + + + + + + + + + + + + ); diff --git a/x-pack/plugins/lens/public/app_plugin/types.ts b/x-pack/plugins/lens/public/app_plugin/types.ts index 263794db96c87..4e53b66a3244f 100644 --- a/x-pack/plugins/lens/public/app_plugin/types.ts +++ b/x-pack/plugins/lens/public/app_plugin/types.ts @@ -32,6 +32,7 @@ import type { DashboardFeatureFlagConfig } from '@kbn/dashboard-plugin/public'; import type { SavedObjectTaggingPluginStart } from '@kbn/saved-objects-tagging-plugin/public'; import type { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public'; import type { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public'; +import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { VisualizeFieldContext, ACTION_VISUALIZE_LENS_FIELD, @@ -161,6 +162,7 @@ export interface LensAppServices { stateTransfer: EmbeddableStateTransfer; navigation: NavigationPublicPluginStart; attributeService: LensAttributeService; + contentManagement: ContentManagementPublicStart; savedObjectsTagging?: SavedObjectTaggingPluginStart; getOriginatingAppName: () => string | undefined; presentationUtil: PresentationUtilPluginStart; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 67dea2f98231c..cfbc6107881e0 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -774,10 +774,15 @@ export class Embeddable }, references, }; - this.updateInput({ attributes: attrs }); + this.updateInput({ attributes: attrs, savedObjectId: undefined }); } } + public updateByRefInput(savedObjectId: string) { + const attrs = this.savedVis; + this.updateInput({ attributes: attrs, savedObjectId }); + } + async openConfingPanel(startDependencies: LensPluginStartDependencies) { const { getEditLensConfiguration } = await import('../async_services'); const Component = await getEditLensConfiguration( @@ -801,6 +806,8 @@ export class Embeddable datasourceId={datasourceId} adaptersTables={this.lensInspector.adapters.tables?.tables} panelId={this.id} + savedObjectId={this.savedVis?.savedObjectId} + updateByRefInput={this.updateByRefInput.bind(this)} /> ); } diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts index 1decd82c4ed19..849605595bb13 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts @@ -30,7 +30,7 @@ function tracksOverlays(root: unknown): root is TracksOverlays { } export async function isActionCompatible(embeddable: IEmbeddable) { - return Boolean(isLensEmbeddable(embeddable) && embeddable.isTextBasedLanguage()); + return Boolean(isLensEmbeddable(embeddable)); } export async function executeAction({ embeddable, startDependencies, overlays, theme }: Context) { From 5bd75d6bb142a0e2ccc566c0ee5f8a633d8b2342 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 11 Sep 2023 17:23:05 +0300 Subject: [PATCH 02/44] Fix types CI check --- x-pack/plugins/lens/public/mocks/services_mock.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/lens/public/mocks/services_mock.tsx b/x-pack/plugins/lens/public/mocks/services_mock.tsx index 8b3cd2e618a68..2f91d6b24c702 100644 --- a/x-pack/plugins/lens/public/mocks/services_mock.tsx +++ b/x-pack/plugins/lens/public/mocks/services_mock.tsx @@ -16,6 +16,7 @@ import { inspectorPluginMock } from '@kbn/inspector-plugin/public/mocks'; import { spacesPluginMock } from '@kbn/spaces-plugin/public/mocks'; import { dashboardPluginMock } from '@kbn/dashboard-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { unifiedSearchPluginMock } from '@kbn/unified-search-plugin/public/mocks'; @@ -185,6 +186,7 @@ export function makeDefaultServices( dataViewEditor: indexPatternEditorPluginMock.createStartContract(), unifiedSearch: unifiedSearchPluginMock.createStartContract(), docLinks: startMock.docLinks, + contentManagement: contentManagementMock.createStartContract(), eventAnnotationService: {} as EventAnnotationServiceType, }; } From 97bb468f1f5511badfdc99c42fe47cf54465e372 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 12 Sep 2023 14:22:23 +0300 Subject: [PATCH 03/44] Remove unecessary query check --- .../unified_histogram/public/chart/chart_config_panel.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx index 5c5ccab277c87..21c6944fb2533 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx @@ -41,7 +41,6 @@ export function ChartConfigPanel({ const [editLensConfigPanel, setEditLensConfigPanel] = useState(null); const previousSuggestion = useRef(undefined); const previousAdapters = useRef | undefined>(undefined); - const previousQuery = useRef(undefined); const updateSuggestion = useCallback( (datasourceState, visualizationState) => { const updatedSuggestion = { @@ -56,9 +55,7 @@ export function ChartConfigPanel({ useEffect(() => { const dataHasChanged = - Boolean(lensTablesAdapter) && - !isEqual(previousAdapters.current, lensTablesAdapter) && - query !== previousQuery?.current; + Boolean(lensTablesAdapter) && !isEqual(previousAdapters.current, lensTablesAdapter); async function fetchLensConfigComponent() { const Component = await services.lens.EditLensConfigPanelApi(); const panel = ( @@ -77,9 +74,6 @@ export function ChartConfigPanel({ setEditLensConfigPanel(panel); previousSuggestion.current = currentSuggestion; previousAdapters.current = lensTablesAdapter; - if (dataHasChanged) { - previousQuery.current = query; - } } const suggestionHasChanged = currentSuggestion?.title !== previousSuggestion?.current?.title; // rerender the component if the data has changed or the suggestion From 21a2b4965df8f4586f3f68593860e218aaba46b3 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 12 Sep 2023 14:33:48 +0300 Subject: [PATCH 04/44] Revert --- .../unified_histogram/public/chart/chart_config_panel.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx index 21c6944fb2533..5c5ccab277c87 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx @@ -41,6 +41,7 @@ export function ChartConfigPanel({ const [editLensConfigPanel, setEditLensConfigPanel] = useState(null); const previousSuggestion = useRef(undefined); const previousAdapters = useRef | undefined>(undefined); + const previousQuery = useRef(undefined); const updateSuggestion = useCallback( (datasourceState, visualizationState) => { const updatedSuggestion = { @@ -55,7 +56,9 @@ export function ChartConfigPanel({ useEffect(() => { const dataHasChanged = - Boolean(lensTablesAdapter) && !isEqual(previousAdapters.current, lensTablesAdapter); + Boolean(lensTablesAdapter) && + !isEqual(previousAdapters.current, lensTablesAdapter) && + query !== previousQuery?.current; async function fetchLensConfigComponent() { const Component = await services.lens.EditLensConfigPanelApi(); const panel = ( @@ -74,6 +77,9 @@ export function ChartConfigPanel({ setEditLensConfigPanel(panel); previousSuggestion.current = currentSuggestion; previousAdapters.current = lensTablesAdapter; + if (dataHasChanged) { + previousQuery.current = query; + } } const suggestionHasChanged = currentSuggestion?.title !== previousSuggestion?.current?.title; // rerender the component if the data has changed or the suggestion From bfaaba517aecc098cc1821352b9fe989d077fd61 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 12 Sep 2023 16:52:08 +0300 Subject: [PATCH 05/44] Fixes in initialization logic --- .../lens_configuration_flyout.tsx | 34 +++++++++++++++---- .../datasources/form_based/form_based.tsx | 12 +++++++ .../text_based/text_based_languages.tsx | 3 ++ x-pack/plugins/lens/public/types.ts | 1 + 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index e36d7a44fc918..6a0fc3be78a2d 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -17,6 +17,7 @@ import { useEuiTheme, EuiCallOut, } from '@elastic/eui'; +import { isEqual } from 'lodash'; import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -72,16 +73,34 @@ export function LensEditConfigurationFlyout({ const activeVisualization = visualizationMap[attributes.visualizationType]; const activeDatasource = datasourceMap[datasourceId]; const { euiTheme } = useEuiTheme(); + const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); + + const attributesChanged: boolean = useMemo(() => { + const attrs = previousAttributes.current; + const prevLayers = datasourceMap[datasourceId].getCurrentLayersState( + attrs.state.datasourceStates[datasourceId] + ); + + const visualizationState = visualization.state; + const datasourceLayers = datasourceMap[datasourceId].getCurrentLayersState( + datasourceStates[datasourceId].state + ); + return ( + !isEqual(visualizationState, attrs.state.visualization) || + !isEqual(datasourceLayers, prevLayers) + ); + }, [datasourceId, datasourceMap, datasourceStates, visualization.state]); const onCancel = useCallback(() => { const attrs = previousAttributes.current; - updateAll?.(attrs.state.datasourceStates[datasourceId], attrs.state.visualization); + if (attributesChanged) { + updateAll?.(attrs.state.datasourceStates[datasourceId], attrs.state.visualization); + } if (savedObjectId) { updateByRefInput?.(savedObjectId); } closeFlyout?.(); - }, [updateAll, datasourceId, savedObjectId, closeFlyout, updateByRefInput]); - const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); + }, [attributesChanged, savedObjectId, closeFlyout, updateAll, datasourceId, updateByRefInput]); const onApply = useCallback(() => { if (savedObjectId) { @@ -117,15 +136,15 @@ export function LensEditConfigurationFlyout({ } closeFlyout?.(); }, [ - activeVisualization, savedObjectId, closeFlyout, - attributes, - datasourceMap, - visualization.state, datasourceStates, + visualization.state, + activeVisualization, + attributes, saveByRef, updateByRefInput, + datasourceMap, ]); const activeData: Record = useMemo(() => { @@ -224,6 +243,7 @@ export function LensEditConfigurationFlyout({ })} iconType="check" data-test-subj="collapseFlyoutButton" + isDisabled={!attributesChanged} > { + const { indexPatternId, ...newLayer } = layer; + + return [id, newLayer]; + }) + ); + return updatedLayers; + }, + removeColumn, initializeDimension( diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index a76e2edd623b0..c4c6a9c2d9dd7 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -304,6 +304,9 @@ export function getTextBasedDatasource({ getLayers(state: TextBasedPrivateState) { return state && state.layers ? Object.keys(state?.layers) : []; }, + getCurrentLayersState(state: TextBasedPrivateState) { + return state?.layers; + }, isTimeBased: (state, indexPatterns) => { if (!state) return false; const { layers } = state; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 13651c3bc5e69..cb9f41923673d 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -340,6 +340,7 @@ export interface Datasource { getNewId: (id: string) => string ) => T; getLayers: (state: T) => string[]; + getCurrentLayersState: (state: T) => unknown; removeColumn: (props: { prevState: T; layerId: string; From b71a16cad948a38bbcb7b353ff9d4af7121f69b7 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 12 Sep 2023 17:06:25 +0300 Subject: [PATCH 06/44] Some cleanup --- .../shared/edit_on_the_fly/lens_configuration_flyout.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 6a0fc3be78a2d..cf92de8704c0a 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -94,6 +94,7 @@ export function LensEditConfigurationFlyout({ const onCancel = useCallback(() => { const attrs = previousAttributes.current; if (attributesChanged) { + // needs to be updated with indexPatternId updateAll?.(attrs.state.datasourceStates[datasourceId], attrs.state.visualization); } if (savedObjectId) { From d99c92670151581f090164162074287ab42a53fb Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 12 Sep 2023 17:42:19 +0300 Subject: [PATCH 07/44] Fix types --- .../shared/edit_on_the_fly/lens_configuration_flyout.tsx | 4 ++-- x-pack/plugins/lens/public/types.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index cf92de8704c0a..5c81cf74cda3a 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -77,12 +77,12 @@ export function LensEditConfigurationFlyout({ const attributesChanged: boolean = useMemo(() => { const attrs = previousAttributes.current; - const prevLayers = datasourceMap[datasourceId].getCurrentLayersState( + const prevLayers = datasourceMap[datasourceId].getCurrentLayersState?.( attrs.state.datasourceStates[datasourceId] ); const visualizationState = visualization.state; - const datasourceLayers = datasourceMap[datasourceId].getCurrentLayersState( + const datasourceLayers = datasourceMap[datasourceId].getCurrentLayersState?.( datasourceStates[datasourceId].state ); return ( diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index cb9f41923673d..d6c8374c8088d 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -340,7 +340,7 @@ export interface Datasource { getNewId: (id: string) => string ) => T; getLayers: (state: T) => string[]; - getCurrentLayersState: (state: T) => unknown; + getCurrentLayersState?: (state: T) => unknown; removeColumn: (props: { prevState: T; layerId: string; From fad8506820769711956686ee555f952862505ff4 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 13 Sep 2023 09:49:36 +0300 Subject: [PATCH 08/44] Fix jest test --- .../shared/edit_on_the_fly/lens_configuration_flyout.test.tsx | 2 +- .../shared/edit_on_the_fly/lens_configuration_flyout.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx index 24c8ced2a7e5f..2d702456ff0b7 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx @@ -125,7 +125,7 @@ describe('LensEditConfigurationFlyout', () => { } as unknown as EditConfigPanelProps; } - it('should call the closeFlyout callback if collapse button is clicked', async () => { + it('should call the closeFlyout callback if cancel button is clicked', async () => { const closeFlyoutSpy = jest.fn(); const props = getDefaultProps(); const newProps = { diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 5c81cf74cda3a..2a1bae5954b75 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -231,6 +231,7 @@ export function LensEditConfigurationFlyout({ aria-label={i18n.translate('xpack.lens.config.cancelFlyoutAriaLabel', { defaultMessage: 'Cancel applied changes', })} + data-test-subj="collapseFlyoutButton" > @@ -243,7 +244,6 @@ export function LensEditConfigurationFlyout({ defaultMessage: 'Apply changes', })} iconType="check" - data-test-subj="collapseFlyoutButton" isDisabled={!attributesChanged} > Date: Wed, 13 Sep 2023 10:37:22 +0300 Subject: [PATCH 09/44] Remove test case --- .../open_lens_config/action.test.tsx | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx index 6c3d98fb3637d..42c1938cc62cb 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx @@ -38,24 +38,6 @@ describe('open config panel action', () => { expect(isCompatible).toBeFalsy(); }); - it('is incompatible with non text based language embeddable', async () => { - const embeddable = { - type: DOC_TYPE, - isTextBasedLanguage: () => false, - } as unknown as IEmbeddable; - const configurablePanelAction = new ConfigureInLensPanelAction( - mockStartDependencies, - overlays, - theme - ); - - const isCompatible = await configurablePanelAction.isCompatible({ - embeddable, - } as ActionExecutionContext<{ embeddable: IEmbeddable }>); - - expect(isCompatible).toBeFalsy(); - }); - it('is compatible with text based language embeddable', async () => { const embeddable = { type: DOC_TYPE, From 34d32a903619a159d3cffd80aaa4ec2d7a909cc2 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 13 Sep 2023 11:28:03 +0300 Subject: [PATCH 10/44] Some refactoring and props renaming --- .../unified_histogram/public/chart/chart.tsx | 1 - .../public/chart/chart_config_panel.tsx | 7 +-- .../get_edit_lens_configuration.tsx | 50 +++++++++++++------ .../lens_configuration_flyout.test.tsx | 5 +- .../lens_configuration_flyout.tsx | 20 ++++---- .../lens/public/embeddable/embeddable.tsx | 9 ++-- 6 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/plugins/unified_histogram/public/chart/chart.tsx b/src/plugins/unified_histogram/public/chart/chart.tsx index 0172b1b6107c1..6a97658575747 100644 --- a/src/plugins/unified_histogram/public/chart/chart.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.tsx @@ -452,7 +452,6 @@ export function Chart({ {...{ services, lensAttributesContext, - dataView, lensTablesAdapter, currentSuggestion, isFlyoutVisible, diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx index 5c5ccab277c87..0560bddd65027 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx @@ -9,7 +9,6 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import type { AggregateQuery, Query } from '@kbn/es-query'; import { isEqual } from 'lodash'; import type { Suggestion } from '@kbn/lens-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; import type { Datatable } from '@kbn/expressions-plugin/common'; import type { UnifiedHistogramServices } from '../types'; @@ -18,7 +17,6 @@ import type { LensAttributesContext } from './utils/get_lens_attributes'; export function ChartConfigPanel({ services, lensAttributesContext, - dataView, lensTablesAdapter, currentSuggestion, isFlyoutVisible, @@ -29,7 +27,6 @@ export function ChartConfigPanel({ }: { services: UnifiedHistogramServices; lensAttributesContext: LensAttributesContext; - dataView: DataView; isFlyoutVisible: boolean; setIsFlyoutVisible: (flag: boolean) => void; lensTablesAdapter?: Record; @@ -64,9 +61,8 @@ export function ChartConfigPanel({ const panel = ( { setIsFlyoutVisible(false); }} @@ -90,7 +86,6 @@ export function ChartConfigPanel({ }, [ lensAttributesContext.attributes, services.lens, - dataView, updateSuggestion, isPlainRecord, currentSuggestion, diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index 8d0eb0f15298f..c4cc17b52e782 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -13,6 +13,7 @@ import { MiddlewareAPI, Dispatch, Action } from '@reduxjs/toolkit'; import { css } from '@emotion/react'; import type { CoreStart } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import type { Datatable } from '@kbn/expressions-plugin/public'; import { isEqual } from 'lodash'; import { RootDragDropProvider } from '@kbn/dom-drag-drop'; import type { LensPluginStartDependencies } from '../../../plugin'; @@ -23,19 +24,38 @@ import { initExisting, initEmpty, } from '../../../state_management'; +import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; import { generateId } from '../../../id_generator'; import type { DatasourceMap, VisualizationMap } from '../../../types'; -import { - LensEditConfigurationFlyout, - type EditConfigPanelProps, -} from './lens_configuration_flyout'; +import { LensEditConfigurationFlyout } from './lens_configuration_flyout'; import { SavedObjectIndexStore, type Document } from '../../../persistence'; import { DOC_TYPE } from '../../../../common/constants'; -export type EditLensConfigurationProps = Omit< - EditConfigPanelProps, - 'startDependencies' | 'coreStart' | 'visualizationMap' | 'datasourceMap' ->; +export interface EditLensConfigurationProps { + /** The attributes of the Lens embeddable */ + attributes: TypedLensByValueInput['attributes']; + /** Callback for updating the visualization and datasources state */ + updatePanelState: (datasourceState: unknown, visualizationState: unknown) => void; + /** Lens visualizations can be either created from ESQL (textBased) or from dataviews (formBased) */ + datasourceId: 'formBased' | 'textBased'; + /** Contains the active data, necessary for some panel configuration such as coloring */ + adaptersTables?: Record; + /** Optional callback called when updating the by reference embeddable */ + updateByRefInput?: (soId: string) => void; + /** Callback for closing the edit flyout */ + closeFlyout?: () => void; + /** Boolean used for adding a flyout wrapper */ + wrapInFlyout?: boolean; + /** Optional parameter for panel identification + * If not given, Lens generates a new one + */ + panelId?: string; + /** Optional parameter for saved object id + * Should be given if the lens embeddable is a by reference one + * (saved in the library) + */ + savedObjectId?: string; +} function LoadingSpinnerWithOverlay() { return ( @@ -88,8 +108,7 @@ export async function getEditLensConfiguration( return ({ attributes, - dataView, - updateAll, + updatePanelState, closeFlyout, wrapInFlyout, datasourceId, @@ -98,9 +117,13 @@ export async function getEditLensConfiguration( savedObjectId, updateByRefInput, }: EditLensConfigurationProps) => { - if (!lensServices || !datasourceMap || !visualizationMap || !dataView.id) { + if (!lensServices || !datasourceMap || !visualizationMap) { return ; } + /** + * During inline editing of a by reference panel, the panel is converted to a by value one. + * When the user applies the changes we save them to the Lens SO + */ const saveByRef = useCallback( async (attrs: Document) => { const savedObjectStore = new SavedObjectIndexStore(lensServices.contentManagement.client); @@ -125,7 +148,7 @@ export async function getEditLensConfiguration( const lensStore: LensRootStore = makeConfigureStore( storeDeps, undefined, - updatingMiddleware(updateAll) + updatingMiddleware(updatePanelState) ); lensStore.dispatch( loadInitial({ @@ -165,8 +188,7 @@ export async function getEditLensConfiguration( const configPanelProps = { attributes, - dataView, - updateAll, + updatePanelState, closeFlyout, datasourceId, adaptersTables, diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx index 2d702456ff0b7..fa2cebc1e43b0 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { EuiFlyoutBody } from '@elastic/eui'; import { mountWithProvider } from '../../../mocks'; import type { Query, AggregateQuery } from '@kbn/es-query'; -import type { DataView } from '@kbn/data-views-plugin/public'; import { coreMock } from '@kbn/core/public/mocks'; import { mockVisualizationMap, @@ -111,11 +110,9 @@ describe('LensEditConfigurationFlyout', () => { references: [], } as unknown as TypedLensByValueInput['attributes']; - const dataView = { id: 'index1', isPersisted: () => true } as unknown as DataView; return { attributes: lensAttributes, - dataView, - updateAll: jest.fn(), + updatePanelState: jest.fn(), coreStart: coreMock.createStart(), startDependencies, visualizationMap, diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 2a1bae5954b75..7d7b87cb796be 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -24,7 +24,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; import type { CoreStart } from '@kbn/core/public'; import type { Datatable } from '@kbn/expressions-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; import type { LensPluginStartDependencies } from '../../../plugin'; import { useLensSelector, selectFramePublicAPI } from '../../../state_management'; import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame/workspace_panel'; @@ -37,15 +36,12 @@ import type { Document } from '../../../persistence'; export interface EditConfigPanelProps { attributes: TypedLensByValueInput['attributes']; - dataView: DataView; - updateAll: (datasourceState: unknown, visualizationState: unknown) => void; + updatePanelState: (datasourceState: unknown, visualizationState: unknown) => void; coreStart: CoreStart; startDependencies: LensPluginStartDependencies; visualizationMap: VisualizationMap; datasourceMap: DatasourceMap; closeFlyout?: () => void; - wrapInFlyout?: boolean; - panelId?: string; savedObjectId?: string; datasourceId: 'formBased' | 'textBased'; adaptersTables?: Record; @@ -55,13 +51,12 @@ export interface EditConfigPanelProps { export function LensEditConfigurationFlyout({ attributes, - dataView, coreStart, startDependencies, visualizationMap, datasourceMap, datasourceId, - updateAll, + updatePanelState, closeFlyout, adaptersTables, saveByRef, @@ -95,13 +90,20 @@ export function LensEditConfigurationFlyout({ const attrs = previousAttributes.current; if (attributesChanged) { // needs to be updated with indexPatternId - updateAll?.(attrs.state.datasourceStates[datasourceId], attrs.state.visualization); + updatePanelState?.(attrs.state.datasourceStates[datasourceId], attrs.state.visualization); } if (savedObjectId) { updateByRefInput?.(savedObjectId); } closeFlyout?.(); - }, [attributesChanged, savedObjectId, closeFlyout, updateAll, datasourceId, updateByRefInput]); + }, [ + attributesChanged, + savedObjectId, + closeFlyout, + updatePanelState, + datasourceId, + updateByRefInput, + ]); const onApply = useCallback(() => { if (savedObjectId) { diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index cfbc6107881e0..28e2b8fb08b1a 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -774,6 +774,11 @@ export class Embeddable }, references, }; + + /** + * SavedObjectId is undefined for by value panels and defined for the by reference ones. + * Here we are converting the by reference panels to by value when user is inline editing + */ this.updateInput({ attributes: attrs, savedObjectId: undefined }); } } @@ -796,13 +801,11 @@ export class Embeddable 'formBased') as EditLensConfigurationProps['datasourceId']; const attributes = this.savedVis as TypedLensByValueInput['attributes']; - const dataView = this.dataViews[0]; if (attributes) { return ( Date: Wed, 13 Sep 2023 12:44:59 +0300 Subject: [PATCH 11/44] Display the action on dashboard edit mode and when the user has the privileges --- x-pack/plugins/lens/public/embeddable/embeddable.tsx | 2 +- .../lens/public/trigger_actions/open_lens_config/helpers.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 28e2b8fb08b1a..f4c46e877b5a6 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -1436,7 +1436,7 @@ export class Embeddable }); } - private getIsEditable() { + public getIsEditable() { return ( this.deps.capabilities.canSaveVisualizations || (!this.inputIsRefType(this.getInput()) && diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts index 849605595bb13..a35f45ebd0465 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts @@ -30,7 +30,9 @@ function tracksOverlays(root: unknown): root is TracksOverlays { } export async function isActionCompatible(embeddable: IEmbeddable) { - return Boolean(isLensEmbeddable(embeddable)); + // display the action only if dashboard is on editable mode + const inDashboardEditMode = embeddable.getInput().viewMode === 'edit'; + return Boolean(isLensEmbeddable(embeddable) && embeddable.getIsEditable() && inDashboardEditMode); } export async function executeAction({ embeddable, startDependencies, overlays, theme }: Context) { From cf620adea41466ceafbb3c18b721379059a27dee Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 13 Sep 2023 15:17:21 +0300 Subject: [PATCH 12/44] Refactoring, fix the activeData refresh bug --- .../public/__mocks__/lens_adapters.ts | 45 ++++ .../public/__mocks__/lens_table_adapter.ts | 40 --- .../unified_histogram/public/chart/chart.tsx | 7 +- .../public/chart/chart_config_panel.test.tsx | 4 +- .../public/chart/chart_config_panel.tsx | 17 +- .../container/hooks/use_state_props.test.ts | 244 ++++++++++-------- .../public/container/hooks/use_state_props.ts | 8 +- .../container/services/state_service.test.ts | 8 +- .../container/services/state_service.ts | 15 +- .../public/container/utils/state_selectors.ts | 2 +- .../public/layout/layout.tsx | 8 +- .../get_edit_lens_configuration.tsx | 14 +- .../lens_configuration_flyout.tsx | 46 +++- .../lens/public/embeddable/embeddable.tsx | 3 +- 14 files changed, 252 insertions(+), 209 deletions(-) create mode 100644 src/plugins/unified_histogram/public/__mocks__/lens_adapters.ts delete mode 100644 src/plugins/unified_histogram/public/__mocks__/lens_table_adapter.ts diff --git a/src/plugins/unified_histogram/public/__mocks__/lens_adapters.ts b/src/plugins/unified_histogram/public/__mocks__/lens_adapters.ts new file mode 100644 index 0000000000000..87e7f782f1bd8 --- /dev/null +++ b/src/plugins/unified_histogram/public/__mocks__/lens_adapters.ts @@ -0,0 +1,45 @@ +/* + * 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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { UnifiedHistogramChartLoadEvent } from '../types'; + +export const lensAdaptersMock = { + tables: { + tables: { + default: { + columns: [ + { + id: 'col-0-1', + meta: { + dimensionName: 'Slice size', + type: 'number', + }, + name: 'Field 1', + }, + { + id: 'col-0-2', + meta: { + dimensionName: 'Slice', + type: 'number', + }, + name: 'Field 2', + }, + ], + rows: [ + { + 'col-0-1': 0, + 'col-0-2': 0, + 'col-0-3': 0, + 'col-0-4': 0, + }, + ], + type: 'datatable', + }, + }, + }, +} as unknown as UnifiedHistogramChartLoadEvent['adapters']; diff --git a/src/plugins/unified_histogram/public/__mocks__/lens_table_adapter.ts b/src/plugins/unified_histogram/public/__mocks__/lens_table_adapter.ts deleted file mode 100644 index 60e38fecbbfae..0000000000000 --- a/src/plugins/unified_histogram/public/__mocks__/lens_table_adapter.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ -import type { Datatable } from '@kbn/expressions-plugin/common'; - -export const lensTablesAdapterMock: Record = { - default: { - columns: [ - { - id: 'col-0-1', - meta: { - dimensionName: 'Slice size', - type: 'number', - }, - name: 'Field 1', - }, - { - id: 'col-0-2', - meta: { - dimensionName: 'Slice', - type: 'number', - }, - name: 'Field 2', - }, - ], - rows: [ - { - 'col-0-1': 0, - 'col-0-2': 0, - 'col-0-3': 0, - 'col-0-4': 0, - }, - ], - type: 'datatable', - }, -}; diff --git a/src/plugins/unified_histogram/public/chart/chart.tsx b/src/plugins/unified_histogram/public/chart/chart.tsx index 6a97658575747..a1c7039539302 100644 --- a/src/plugins/unified_histogram/public/chart/chart.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.tsx @@ -17,7 +17,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import type { EmbeddableComponentProps, Suggestion } from '@kbn/lens-plugin/public'; -import type { Datatable } from '@kbn/expressions-plugin/common'; import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; import type { LensEmbeddableInput } from '@kbn/lens-plugin/public'; import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; @@ -68,7 +67,7 @@ export interface ChartProps { disableTriggers?: LensEmbeddableInput['disableTriggers']; disabledActions?: LensEmbeddableInput['disabledActions']; input$?: UnifiedHistogramInput$; - lensTablesAdapter?: Record; + lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; isOnHistogramMode?: boolean; onResetChartHeight?: () => void; onChartHiddenChange?: (chartHidden: boolean) => void; @@ -105,7 +104,7 @@ export function Chart({ disableTriggers, disabledActions, input$: originalInput$, - lensTablesAdapter, + lensAdapters, isOnHistogramMode, onResetChartHeight, onChartHiddenChange, @@ -452,7 +451,7 @@ export function Chart({ {...{ services, lensAttributesContext, - lensTablesAdapter, + lensAdapters, currentSuggestion, isFlyoutVisible, setIsFlyoutVisible, diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.test.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.test.tsx index ef673826672ea..5238fc0ac12bb 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.test.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.test.tsx @@ -13,7 +13,7 @@ import { act } from 'react-dom/test-utils'; import { setTimeout } from 'timers/promises'; import { dataViewWithTimefieldMock } from '../__mocks__/data_view_with_timefield'; import { unifiedHistogramServicesMock } from '../__mocks__/services'; -import { lensTablesAdapterMock } from '../__mocks__/lens_table_adapter'; +import { lensAdaptersMock } from '../__mocks__/lens_adapters'; import { ChartConfigPanel } from './chart_config_panel'; import type { LensAttributesContext } from './utils/get_lens_attributes'; @@ -34,7 +34,7 @@ describe('ChartConfigPanel', () => { isFlyoutVisible: true, setIsFlyoutVisible: jest.fn(), isPlainRecord: true, - lensTablesAdapter: lensTablesAdapterMock, + lensAdapters: lensAdaptersMock, query: { esql: 'from test', }, diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx index 0560bddd65027..31108016d1d55 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx @@ -11,13 +11,13 @@ import { isEqual } from 'lodash'; import type { Suggestion } from '@kbn/lens-plugin/public'; import type { Datatable } from '@kbn/expressions-plugin/common'; -import type { UnifiedHistogramServices } from '../types'; +import type { UnifiedHistogramServices, UnifiedHistogramChartLoadEvent } from '../types'; import type { LensAttributesContext } from './utils/get_lens_attributes'; export function ChartConfigPanel({ services, lensAttributesContext, - lensTablesAdapter, + lensAdapters, currentSuggestion, isFlyoutVisible, setIsFlyoutVisible, @@ -29,7 +29,7 @@ export function ChartConfigPanel({ lensAttributesContext: LensAttributesContext; isFlyoutVisible: boolean; setIsFlyoutVisible: (flag: boolean) => void; - lensTablesAdapter?: Record; + lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; currentSuggestion?: Suggestion; isPlainRecord?: boolean; query?: Query | AggregateQuery; @@ -52,17 +52,16 @@ export function ChartConfigPanel({ ); useEffect(() => { + const tablesAdapters = lensAdapters?.tables?.tables; const dataHasChanged = - Boolean(lensTablesAdapter) && - !isEqual(previousAdapters.current, lensTablesAdapter) && - query !== previousQuery?.current; + Boolean(tablesAdapters) && !isEqual(previousAdapters.current, tablesAdapters); async function fetchLensConfigComponent() { const Component = await services.lens.EditLensConfigPanelApi(); const panel = ( { setIsFlyoutVisible(false); }} @@ -72,7 +71,7 @@ export function ChartConfigPanel({ ); setEditLensConfigPanel(panel); previousSuggestion.current = currentSuggestion; - previousAdapters.current = lensTablesAdapter; + previousAdapters.current = tablesAdapters; if (dataHasChanged) { previousQuery.current = query; } @@ -91,8 +90,8 @@ export function ChartConfigPanel({ currentSuggestion, query, isFlyoutVisible, - lensTablesAdapter, setIsFlyoutVisible, + lensAdapters, ]); return isPlainRecord ? editLensConfigPanel : null; diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts index c9ab0d5220aed..6aa6f0d762a68 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts @@ -15,7 +15,7 @@ import { UnifiedHistogramFetchStatus } from '../../types'; import { dataViewMock } from '../../__mocks__/data_view'; import { dataViewWithTimefieldMock } from '../../__mocks__/data_view_with_timefield'; import { currentSuggestionMock } from '../../__mocks__/suggestions'; -import { lensTablesAdapterMock } from '../../__mocks__/lens_table_adapter'; +import { lensAdaptersMock } from '../../__mocks__/lens_adapters'; import { unifiedHistogramServicesMock } from '../../__mocks__/services'; import { createStateService, @@ -29,7 +29,7 @@ describe('useStateProps', () => { breakdownField: 'bytes', chartHidden: false, lensRequestAdapter: new RequestAdapter(), - lensTablesAdapter: lensTablesAdapterMock, + lensAdapters: lensAdaptersMock, timeInterval: 'auto', topPanelHeight: 100, totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, @@ -84,35 +84,39 @@ describe('useStateProps', () => { "total": undefined, }, "isPlainRecord": false, - "lensTablesAdapter": Object { - "default": Object { - "columns": Array [ - Object { - "id": "col-0-1", - "meta": Object { - "dimensionName": "Slice size", - "type": "number", - }, - "name": "Field 1", + "lensAdapters": Object { + "tables": Object { + "tables": Object { + "default": Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Slice size", + "type": "number", + }, + "name": "Field 1", + }, + Object { + "id": "col-0-2", + "meta": Object { + "dimensionName": "Slice", + "type": "number", + }, + "name": "Field 2", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + "col-0-2": 0, + "col-0-3": 0, + "col-0-4": 0, + }, + ], + "type": "datatable", }, - Object { - "id": "col-0-2", - "meta": Object { - "dimensionName": "Slice", - "type": "number", - }, - "name": "Field 2", - }, - ], - "rows": Array [ - Object { - "col-0-1": 0, - "col-0-2": 0, - "col-0-3": 0, - "col-0-4": 0, - }, - ], - "type": "datatable", + }, }, }, "onBreakdownFieldChange": [Function], @@ -159,35 +163,39 @@ describe('useStateProps', () => { "total": undefined, }, "isPlainRecord": true, - "lensTablesAdapter": Object { - "default": Object { - "columns": Array [ - Object { - "id": "col-0-1", - "meta": Object { - "dimensionName": "Slice size", - "type": "number", - }, - "name": "Field 1", - }, - Object { - "id": "col-0-2", - "meta": Object { - "dimensionName": "Slice", - "type": "number", - }, - "name": "Field 2", - }, - ], - "rows": Array [ - Object { - "col-0-1": 0, - "col-0-2": 0, - "col-0-3": 0, - "col-0-4": 0, + "lensAdapters": Object { + "tables": Object { + "tables": Object { + "default": Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Slice size", + "type": "number", + }, + "name": "Field 1", + }, + Object { + "id": "col-0-2", + "meta": Object { + "dimensionName": "Slice", + "type": "number", + }, + "name": "Field 2", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + "col-0-2": 0, + "col-0-3": 0, + "col-0-4": 0, + }, + ], + "type": "datatable", }, - ], - "type": "datatable", + }, }, }, "onBreakdownFieldChange": [Function], @@ -255,35 +263,39 @@ describe('useStateProps', () => { "total": undefined, }, "isPlainRecord": false, - "lensTablesAdapter": Object { - "default": Object { - "columns": Array [ - Object { - "id": "col-0-1", - "meta": Object { - "dimensionName": "Slice size", - "type": "number", - }, - "name": "Field 1", + "lensAdapters": Object { + "tables": Object { + "tables": Object { + "default": Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Slice size", + "type": "number", + }, + "name": "Field 1", + }, + Object { + "id": "col-0-2", + "meta": Object { + "dimensionName": "Slice", + "type": "number", + }, + "name": "Field 2", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + "col-0-2": 0, + "col-0-3": 0, + "col-0-4": 0, + }, + ], + "type": "datatable", }, - Object { - "id": "col-0-2", - "meta": Object { - "dimensionName": "Slice", - "type": "number", - }, - "name": "Field 2", - }, - ], - "rows": Array [ - Object { - "col-0-1": 0, - "col-0-2": 0, - "col-0-3": 0, - "col-0-4": 0, - }, - ], - "type": "datatable", + }, }, }, "onBreakdownFieldChange": [Function], @@ -327,35 +339,39 @@ describe('useStateProps', () => { "total": undefined, }, "isPlainRecord": false, - "lensTablesAdapter": Object { - "default": Object { - "columns": Array [ - Object { - "id": "col-0-1", - "meta": Object { - "dimensionName": "Slice size", - "type": "number", - }, - "name": "Field 1", - }, - Object { - "id": "col-0-2", - "meta": Object { - "dimensionName": "Slice", - "type": "number", - }, - "name": "Field 2", - }, - ], - "rows": Array [ - Object { - "col-0-1": 0, - "col-0-2": 0, - "col-0-3": 0, - "col-0-4": 0, + "lensAdapters": Object { + "tables": Object { + "tables": Object { + "default": Object { + "columns": Array [ + Object { + "id": "col-0-1", + "meta": Object { + "dimensionName": "Slice size", + "type": "number", + }, + "name": "Field 1", + }, + Object { + "id": "col-0-2", + "meta": Object { + "dimensionName": "Slice", + "type": "number", + }, + "name": "Field 2", + }, + ], + "rows": Array [ + Object { + "col-0-1": 0, + "col-0-2": 0, + "col-0-3": 0, + "col-0-4": 0, + }, + ], + "type": "datatable", }, - ], - "type": "datatable", + }, }, }, "onBreakdownFieldChange": [Function], diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts index a5845731cf12e..2daa90eddf26b 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts @@ -23,7 +23,7 @@ import { timeIntervalSelector, totalHitsResultSelector, totalHitsStatusSelector, - lensTablesAdapterSelector, + lensAdaptersSelector, } from '../utils/state_selectors'; import { useStateSelector } from '../utils/use_state_selector'; @@ -45,7 +45,7 @@ export const useStateProps = ({ const timeInterval = useStateSelector(stateService?.state$, timeIntervalSelector); const totalHitsResult = useStateSelector(stateService?.state$, totalHitsResultSelector); const totalHitsStatus = useStateSelector(stateService?.state$, totalHitsStatusSelector); - const lensTablesAdapter = useStateSelector(stateService?.state$, lensTablesAdapterSelector); + const lensAdapters = useStateSelector(stateService?.state$, lensAdaptersSelector); /** * Contexts */ @@ -140,7 +140,7 @@ export const useStateProps = ({ (event: UnifiedHistogramChartLoadEvent) => { // We need to store the Lens request adapter in order to inspect its requests stateService?.setLensRequestAdapter(event.adapters.requests); - stateService?.setLensTablesAdapter(event.adapters.tables?.tables); + stateService?.setLensAdapters(event.adapters); }, [stateService] ); @@ -176,7 +176,7 @@ export const useStateProps = ({ breakdown, request, isPlainRecord, - lensTablesAdapter, + lensAdapters, onTopPanelHeightChange, onTimeIntervalChange, onTotalHitsChange, diff --git a/src/plugins/unified_histogram/public/container/services/state_service.test.ts b/src/plugins/unified_histogram/public/container/services/state_service.test.ts index eb7232e889037..82daf76fc58c5 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.test.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.test.ts @@ -9,7 +9,7 @@ import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { UnifiedHistogramFetchStatus } from '../..'; import { unifiedHistogramServicesMock } from '../../__mocks__/services'; -import { lensTablesAdapterMock } from '../../__mocks__/lens_table_adapter'; +import { lensAdaptersMock } from '../../__mocks__/lens_adapters'; import { getChartHidden, getTopPanelHeight, @@ -47,7 +47,7 @@ describe('UnifiedHistogramStateService', () => { breakdownField: 'bytes', chartHidden: false, lensRequestAdapter: new RequestAdapter(), - lensTablesAdapter: lensTablesAdapterMock, + lensAdapters: lensAdaptersMock, timeInterval: 'auto', topPanelHeight: 100, totalHitsStatus: UnifiedHistogramFetchStatus.uninitialized, @@ -136,8 +136,8 @@ describe('UnifiedHistogramStateService', () => { expect(state).toEqual(newState); stateService.setLensRequestAdapter(undefined); newState = { ...newState, lensRequestAdapter: undefined }; - stateService.setLensTablesAdapter(undefined); - newState = { ...newState, lensTablesAdapter: undefined }; + stateService.setLensAdapters(undefined); + newState = { ...newState, lensAdapters: undefined }; expect(state).toEqual(newState); stateService.setTotalHits({ totalHitsStatus: UnifiedHistogramFetchStatus.complete, diff --git a/src/plugins/unified_histogram/public/container/services/state_service.ts b/src/plugins/unified_histogram/public/container/services/state_service.ts index 1fd0905d86c7a..655918036e7b9 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.ts @@ -8,10 +8,9 @@ import type { RequestAdapter } from '@kbn/inspector-plugin/common'; import type { Suggestion } from '@kbn/lens-plugin/public'; -import type { Datatable } from '@kbn/expressions-plugin/common'; import { BehaviorSubject, Observable } from 'rxjs'; import { UnifiedHistogramFetchStatus } from '../..'; -import type { UnifiedHistogramServices } from '../../types'; +import type { UnifiedHistogramServices, UnifiedHistogramChartLoadEvent } from '../../types'; import { getBreakdownField, getChartHidden, @@ -42,9 +41,9 @@ export interface UnifiedHistogramState { */ lensRequestAdapter: RequestAdapter | undefined; /** - * The current Lens request table + * The current Lens adapters */ - lensTablesAdapter?: Record; + lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; /** * The current time interval of the chart */ @@ -114,9 +113,9 @@ export interface UnifiedHistogramStateService { */ setLensRequestAdapter: (lensRequestAdapter: RequestAdapter | undefined) => void; /** - * Sets the current Lens tables + * Sets the current Lens adapters */ - setLensTablesAdapter: (lensTablesAdapter: Record | undefined) => void; + setLensAdapters: (lensAdapters: UnifiedHistogramChartLoadEvent['adapters'] | undefined) => void; /** * Sets the current total hits status and result */ @@ -199,8 +198,8 @@ export const createStateService = ( updateState({ lensRequestAdapter }); }, - setLensTablesAdapter: (lensTablesAdapter: Record | undefined) => { - updateState({ lensTablesAdapter }); + setLensAdapters: (lensAdapters: UnifiedHistogramChartLoadEvent['adapters'] | undefined) => { + updateState({ lensAdapters }); }, setTotalHits: (totalHits: { diff --git a/src/plugins/unified_histogram/public/container/utils/state_selectors.ts b/src/plugins/unified_histogram/public/container/utils/state_selectors.ts index 80e809f4fc38f..9b91cf472cdd8 100644 --- a/src/plugins/unified_histogram/public/container/utils/state_selectors.ts +++ b/src/plugins/unified_histogram/public/container/utils/state_selectors.ts @@ -15,4 +15,4 @@ export const topPanelHeightSelector = (state: UnifiedHistogramState) => state.to export const totalHitsResultSelector = (state: UnifiedHistogramState) => state.totalHitsResult; export const totalHitsStatusSelector = (state: UnifiedHistogramState) => state.totalHitsStatus; export const currentSuggestionSelector = (state: UnifiedHistogramState) => state.currentSuggestion; -export const lensTablesAdapterSelector = (state: UnifiedHistogramState) => state.lensTablesAdapter; +export const lensAdaptersSelector = (state: UnifiedHistogramState) => state.lensAdapters; diff --git a/src/plugins/unified_histogram/public/layout/layout.tsx b/src/plugins/unified_histogram/public/layout/layout.tsx index 95661ed9b3f2f..99bd5196ed3d5 100644 --- a/src/plugins/unified_histogram/public/layout/layout.tsx +++ b/src/plugins/unified_histogram/public/layout/layout.tsx @@ -11,7 +11,7 @@ import { PropsWithChildren, ReactElement, RefObject } from 'react'; import React, { useMemo } from 'react'; import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; import { css } from '@emotion/css'; -import type { Datatable, DatatableColumn } from '@kbn/expressions-plugin/common'; +import type { DatatableColumn } from '@kbn/expressions-plugin/common'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import type { EmbeddableComponentProps, @@ -83,7 +83,7 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren * Context object for the hits count -- leave undefined to hide the hits count */ hits?: UnifiedHistogramHitsContext; - lensTablesAdapter?: Record; + lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; /** * Context object for the chart -- leave undefined to hide the chart */ @@ -180,7 +180,7 @@ export const UnifiedHistogramLayout = ({ columns, request, hits, - lensTablesAdapter, + lensAdapters, chart: originalChart, breakdown, resizeRef, @@ -289,7 +289,7 @@ export const UnifiedHistogramLayout = ({ onChartLoad={onChartLoad} onFilter={onFilter} onBrushEnd={onBrushEnd} - lensTablesAdapter={lensTablesAdapter} + lensAdapters={lensAdapters} isOnHistogramMode={isOnHistogramMode} withDefaultActions={withDefaultActions} /> diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index c4cc17b52e782..8eafbd37eaf6d 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -11,12 +11,13 @@ import { i18n } from '@kbn/i18n'; import { Provider } from 'react-redux'; import { MiddlewareAPI, Dispatch, Action } from '@reduxjs/toolkit'; import { css } from '@emotion/react'; +import type { Observable } from 'rxjs'; import type { CoreStart } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; -import type { Datatable } from '@kbn/expressions-plugin/public'; import { isEqual } from 'lodash'; import { RootDragDropProvider } from '@kbn/dom-drag-drop'; import type { LensPluginStartDependencies } from '../../../plugin'; +import type { LensEmbeddableOutput } from '../../../embeddable'; import { makeConfigureStore, LensRootStore, @@ -29,6 +30,7 @@ import { generateId } from '../../../id_generator'; import type { DatasourceMap, VisualizationMap } from '../../../types'; import { LensEditConfigurationFlyout } from './lens_configuration_flyout'; import { SavedObjectIndexStore, type Document } from '../../../persistence'; +import type { LensInspector } from '../../../lens_inspector_service'; import { DOC_TYPE } from '../../../../common/constants'; export interface EditLensConfigurationProps { @@ -38,8 +40,10 @@ export interface EditLensConfigurationProps { updatePanelState: (datasourceState: unknown, visualizationState: unknown) => void; /** Lens visualizations can be either created from ESQL (textBased) or from dataviews (formBased) */ datasourceId: 'formBased' | 'textBased'; + /** Embeddable output observable, useful for dashboard flyout */ + output$?: Observable; /** Contains the active data, necessary for some panel configuration such as coloring */ - adaptersTables?: Record; + lensAdapters?: LensInspector['adapters']; /** Optional callback called when updating the by reference embeddable */ updateByRefInput?: (soId: string) => void; /** Callback for closing the edit flyout */ @@ -112,9 +116,10 @@ export async function getEditLensConfiguration( closeFlyout, wrapInFlyout, datasourceId, - adaptersTables, panelId, savedObjectId, + output$, + lensAdapters, updateByRefInput, }: EditLensConfigurationProps) => { if (!lensServices || !datasourceMap || !visualizationMap) { @@ -191,10 +196,11 @@ export async function getEditLensConfiguration( updatePanelState, closeFlyout, datasourceId, - adaptersTables, coreStart, startDependencies, visualizationMap, + output$, + lensAdapters, datasourceMap, saveByRef, savedObjectId, diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 7d7b87cb796be..bd008af30824d 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo, useCallback, useRef } from 'react'; +import React, { useMemo, useCallback, useRef, useEffect, useState } from 'react'; import { EuiButtonEmpty, EuiButton, @@ -18,6 +18,7 @@ import { EuiCallOut, } from '@elastic/eui'; import { isEqual } from 'lodash'; +import type { Observable } from 'rxjs'; import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -30,6 +31,8 @@ import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame import type { DatasourceMap, VisualizationMap } from '../../../types'; import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; +import type { LensEmbeddableOutput } from '../../../embeddable'; +import type { LensInspector } from '../../../lens_inspector_service'; import { ConfigPanelWrapper } from '../../../editor_frame_service/editor_frame/config_panel/config_panel'; import { extractReferencesFromState } from '../../../utils'; import type { Document } from '../../../persistence'; @@ -41,14 +44,27 @@ export interface EditConfigPanelProps { startDependencies: LensPluginStartDependencies; visualizationMap: VisualizationMap; datasourceMap: DatasourceMap; + output$?: Observable; + lensAdapters?: LensInspector['adapters']; closeFlyout?: () => void; savedObjectId?: string; datasourceId: 'formBased' | 'textBased'; - adaptersTables?: Record; saveByRef?: (attrs: Document) => void; updateByRefInput?: (soId: string) => void; } +const computeActiveData = (layers: string[], lensAdapters?: LensInspector['adapters']) => { + const dataTable: Record = {}; + const tables = lensAdapters?.tables?.tables as Record; + const [table] = Object.values(tables || {}); + layers.forEach((layer) => { + if (table) { + dataTable[layer] = table; + } + }); + return dataTable; +}; + export function LensEditConfigurationFlyout({ attributes, coreStart, @@ -58,11 +74,13 @@ export function LensEditConfigurationFlyout({ datasourceId, updatePanelState, closeFlyout, - adaptersTables, saveByRef, savedObjectId, updateByRefInput, + output$, + lensAdapters, }: EditConfigPanelProps) { + const [activeData, setActiveData] = useState>({}); const previousAttributes = useRef(attributes); const datasourceState = attributes.state.datasourceStates[datasourceId]; const activeVisualization = visualizationMap[attributes.visualizationType]; @@ -70,6 +88,17 @@ export function LensEditConfigurationFlyout({ const { euiTheme } = useEuiTheme(); const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); + useEffect(() => { + const layers = activeDatasource.getLayers(datasourceState); + const s = output$?.subscribe(() => { + setActiveData(computeActiveData(layers, lensAdapters)); + }); + if (!output$) { + setActiveData(computeActiveData(layers, lensAdapters)); + } + return () => s?.unsubscribe(); + }, [activeDatasource, lensAdapters, datasourceState, output$]); + const attributesChanged: boolean = useMemo(() => { const attrs = previousAttributes.current; const prevLayers = datasourceMap[datasourceId].getCurrentLayersState?.( @@ -89,7 +118,6 @@ export function LensEditConfigurationFlyout({ const onCancel = useCallback(() => { const attrs = previousAttributes.current; if (attributesChanged) { - // needs to be updated with indexPatternId updatePanelState?.(attrs.state.datasourceStates[datasourceId], attrs.state.visualization); } if (savedObjectId) { @@ -150,16 +178,6 @@ export function LensEditConfigurationFlyout({ datasourceMap, ]); - const activeData: Record = useMemo(() => { - return {}; - }, []); - const layers = activeDatasource.getLayers(datasourceState); - layers.forEach((layer) => { - if (adaptersTables) { - activeData[layer] = Object.values(adaptersTables)[0]; - } - }); - const framePublicAPI = useLensSelector((state) => { const newState = { ...state, diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index f4c46e877b5a6..77a96b67a15b7 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -807,7 +807,8 @@ export class Embeddable attributes={attributes} updatePanelState={this.updateVisualization.bind(this)} datasourceId={datasourceId} - adaptersTables={this.lensInspector.adapters.tables?.tables} + lensAdapters={this.lensInspector.adapters} + output$={this.getOutput$()} panelId={this.id} savedObjectId={this.savedVis?.savedObjectId} updateByRefInput={this.updateByRefInput.bind(this)} From 0b18c38ec257559426b5e201c7878b10fdff5df4 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 13 Sep 2023 15:52:02 +0300 Subject: [PATCH 13/44] Remove the reset button from Discover --- .../get_edit_lens_configuration.tsx | 4 + .../lens_configuration_flyout.tsx | 80 ++++++++++++------- .../lens/public/embeddable/embeddable.tsx | 1 + 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index 8eafbd37eaf6d..fc4150ed84138 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -59,6 +59,8 @@ export interface EditLensConfigurationProps { * (saved in the library) */ savedObjectId?: string; + /** When set to true it enables resetting to the previous state */ + enablesResetButton?: boolean; } function LoadingSpinnerWithOverlay() { @@ -121,6 +123,7 @@ export async function getEditLensConfiguration( output$, lensAdapters, updateByRefInput, + enablesResetButton, }: EditLensConfigurationProps) => { if (!lensServices || !datasourceMap || !visualizationMap) { return ; @@ -205,6 +208,7 @@ export async function getEditLensConfiguration( saveByRef, savedObjectId, updateByRefInput, + enablesResetButton, }; return getWrapper( diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index bd008af30824d..50754d8fc2d6d 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -51,6 +51,7 @@ export interface EditConfigPanelProps { datasourceId: 'formBased' | 'textBased'; saveByRef?: (attrs: Document) => void; updateByRefInput?: (soId: string) => void; + enablesResetButton?: boolean; } const computeActiveData = (layers: string[], lensAdapters?: LensInspector['adapters']) => { @@ -79,6 +80,7 @@ export function LensEditConfigurationFlyout({ updateByRefInput, output$, lensAdapters, + enablesResetButton, }: EditConfigPanelProps) { const [activeData, setActiveData] = useState>({}); const previousAttributes = useRef(attributes); @@ -243,36 +245,54 @@ export function LensEditConfigurationFlyout({ - - - - - - - - - - - - + {Boolean(enablesResetButton) && ( + + + + + + + + + + + + + )} + {!Boolean(enablesResetButton) && ( + + + + )} ); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 77a96b67a15b7..5913798676c1e 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -808,6 +808,7 @@ export class Embeddable updatePanelState={this.updateVisualization.bind(this)} datasourceId={datasourceId} lensAdapters={this.lensInspector.adapters} + enablesResetButton={true} output$={this.getOutput$()} panelId={this.id} savedObjectId={this.savedVis?.savedObjectId} From 78f0ea2633da0576f7cd880b89dc35a61a93e7d0 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 14 Sep 2023 10:21:20 +0300 Subject: [PATCH 14/44] Change the unified histogram implementation to get the embeddable output$ --- .../unified_histogram/public/chart/chart.tsx | 15 ++++++-- .../public/chart/chart_config_panel.tsx | 11 ++++-- .../public/chart/histogram.tsx | 19 ++++++++--- .../container/hooks/use_state_props.test.ts | 5 ++- .../public/container/hooks/use_state_props.ts | 14 +++++++- .../container/services/state_service.test.ts | 3 ++ .../container/services/state_service.ts | 14 +++++++- .../public/container/utils/state_selectors.ts | 2 ++ .../public/layout/layout.tsx | 5 +++ .../lens_configuration_flyout.tsx | 34 +++++++------------ .../lens/public/embeddable/embeddable.tsx | 9 +++-- x-pack/plugins/lens/public/index.ts | 7 +++- 12 files changed, 103 insertions(+), 35 deletions(-) diff --git a/src/plugins/unified_histogram/public/chart/chart.tsx b/src/plugins/unified_histogram/public/chart/chart.tsx index a1c7039539302..0473b8313dee8 100644 --- a/src/plugins/unified_histogram/public/chart/chart.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.tsx @@ -7,6 +7,7 @@ */ import React, { ReactElement, useMemo, useState, useEffect, useCallback, memo } from 'react'; +import type { Observable } from 'rxjs'; import { EuiButtonIcon, EuiContextMenu, @@ -16,7 +17,11 @@ import { EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { EmbeddableComponentProps, Suggestion } from '@kbn/lens-plugin/public'; +import type { + EmbeddableComponentProps, + Suggestion, + LensEmbeddableOutput, +} from '@kbn/lens-plugin/public'; import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/public'; import type { LensEmbeddableInput } from '@kbn/lens-plugin/public'; import type { AggregateQuery, Filter, Query, TimeRange } from '@kbn/es-query'; @@ -68,6 +73,7 @@ export interface ChartProps { disabledActions?: LensEmbeddableInput['disabledActions']; input$?: UnifiedHistogramInput$; lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; + lensEmbeddableOutput$?: Observable; isOnHistogramMode?: boolean; onResetChartHeight?: () => void; onChartHiddenChange?: (chartHidden: boolean) => void; @@ -75,7 +81,10 @@ export interface ChartProps { onBreakdownFieldChange?: (breakdownField: DataViewField | undefined) => void; onSuggestionChange?: (suggestion: Suggestion | undefined) => void; onTotalHitsChange?: (status: UnifiedHistogramFetchStatus, result?: number | Error) => void; - onChartLoad?: (event: UnifiedHistogramChartLoadEvent) => void; + onChartLoad?: ( + event: UnifiedHistogramChartLoadEvent, + lensEmbeddableOutput$?: Observable + ) => void; onFilter?: LensEmbeddableInput['onFilter']; onBrushEnd?: LensEmbeddableInput['onBrushEnd']; withDefaultActions: EmbeddableComponentProps['withDefaultActions']; @@ -105,6 +114,7 @@ export function Chart({ disabledActions, input$: originalInput$, lensAdapters, + lensEmbeddableOutput$, isOnHistogramMode, onResetChartHeight, onChartHiddenChange, @@ -452,6 +462,7 @@ export function Chart({ services, lensAttributesContext, lensAdapters, + lensEmbeddableOutput$, currentSuggestion, isFlyoutVisible, setIsFlyoutVisible, diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx index 31108016d1d55..4a8973e746e28 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx @@ -6,9 +6,10 @@ * Side Public License, v 1. */ import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { Observable } from 'rxjs'; import type { AggregateQuery, Query } from '@kbn/es-query'; import { isEqual } from 'lodash'; -import type { Suggestion } from '@kbn/lens-plugin/public'; +import type { LensEmbeddableOutput, Suggestion } from '@kbn/lens-plugin/public'; import type { Datatable } from '@kbn/expressions-plugin/common'; import type { UnifiedHistogramServices, UnifiedHistogramChartLoadEvent } from '../types'; @@ -18,6 +19,7 @@ export function ChartConfigPanel({ services, lensAttributesContext, lensAdapters, + lensEmbeddableOutput$, currentSuggestion, isFlyoutVisible, setIsFlyoutVisible, @@ -30,6 +32,7 @@ export function ChartConfigPanel({ isFlyoutVisible: boolean; setIsFlyoutVisible: (flag: boolean) => void; lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; + lensEmbeddableOutput$?: Observable; currentSuggestion?: Suggestion; isPlainRecord?: boolean; query?: Query | AggregateQuery; @@ -54,7 +57,9 @@ export function ChartConfigPanel({ useEffect(() => { const tablesAdapters = lensAdapters?.tables?.tables; const dataHasChanged = - Boolean(tablesAdapters) && !isEqual(previousAdapters.current, tablesAdapters); + Boolean(tablesAdapters) && + !isEqual(previousAdapters.current, tablesAdapters) && + query !== previousQuery?.current; async function fetchLensConfigComponent() { const Component = await services.lens.EditLensConfigPanelApi(); const panel = ( @@ -62,6 +67,7 @@ export function ChartConfigPanel({ attributes={lensAttributesContext.attributes} updatePanelState={updateSuggestion} lensAdapters={lensAdapters} + output$={lensEmbeddableOutput$} closeFlyout={() => { setIsFlyoutVisible(false); }} @@ -92,6 +98,7 @@ export function ChartConfigPanel({ isFlyoutVisible, setIsFlyoutVisible, lensAdapters, + lensEmbeddableOutput$, ]); return isPlainRecord ? editLensConfigPanel : null; diff --git a/src/plugins/unified_histogram/public/chart/histogram.tsx b/src/plugins/unified_histogram/public/chart/histogram.tsx index 0046e0b6a87bd..6d3af6aea3e8e 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.tsx @@ -14,7 +14,11 @@ import type { DefaultInspectorAdapters, Datatable } from '@kbn/expressions-plugi import type { IKibanaSearchResponse } from '@kbn/data-plugin/public'; import type { estypes } from '@elastic/elasticsearch'; import type { TimeRange } from '@kbn/es-query'; -import type { EmbeddableComponentProps, LensEmbeddableInput } from '@kbn/lens-plugin/public'; +import type { + EmbeddableComponentProps, + LensEmbeddableInput, + LensEmbeddableOutput, +} from '@kbn/lens-plugin/public'; import { RequestStatus } from '@kbn/inspector-plugin/public'; import type { Observable } from 'rxjs'; import { @@ -47,7 +51,10 @@ export interface HistogramProps { disableTriggers?: LensEmbeddableInput['disableTriggers']; disabledActions?: LensEmbeddableInput['disabledActions']; onTotalHitsChange?: (status: UnifiedHistogramFetchStatus, result?: number | Error) => void; - onChartLoad?: (event: UnifiedHistogramChartLoadEvent) => void; + onChartLoad?: ( + event: UnifiedHistogramChartLoadEvent, + lensEmbeddableOutput$?: Observable + ) => void; onFilter?: LensEmbeddableInput['onFilter']; onBrushEnd?: LensEmbeddableInput['onBrushEnd']; withDefaultActions: EmbeddableComponentProps['withDefaultActions']; @@ -118,7 +125,11 @@ export function Histogram({ }, [attributes, containerHeight, containerWidth]); const onLoad = useStableCallback( - (isLoading: boolean, adapters: Partial | undefined) => { + ( + isLoading: boolean, + adapters: Partial | undefined, + lensEmbeddableOutput$?: Observable + ) => { const lensRequest = adapters?.requests?.getRequests()[0]; const requestFailed = lensRequest?.status === RequestStatus.ERROR; const json = lensRequest?.response?.json as @@ -155,7 +166,7 @@ export function Histogram({ setBucketInterval(newBucketInterval); } - onChartLoad?.({ adapters: adapters ?? {} }); + onChartLoad?.({ adapters: adapters ?? {} }, lensEmbeddableOutput$); } ); diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts index 6aa6f0d762a68..d32235f06f356 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.test.ts @@ -5,7 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - import { DataView, DataViewField, DataViewType } from '@kbn/data-views-plugin/common'; import { RequestAdapter } from '@kbn/inspector-plugin/common'; import { Suggestion } from '@kbn/lens-plugin/public'; @@ -119,6 +118,7 @@ describe('useStateProps', () => { }, }, }, + "lensEmbeddableOutput$": undefined, "onBreakdownFieldChange": [Function], "onChartHiddenChange": [Function], "onChartLoad": [Function], @@ -198,6 +198,7 @@ describe('useStateProps', () => { }, }, }, + "lensEmbeddableOutput$": undefined, "onBreakdownFieldChange": [Function], "onChartHiddenChange": [Function], "onChartLoad": [Function], @@ -298,6 +299,7 @@ describe('useStateProps', () => { }, }, }, + "lensEmbeddableOutput$": undefined, "onBreakdownFieldChange": [Function], "onChartHiddenChange": [Function], "onChartLoad": [Function], @@ -374,6 +376,7 @@ describe('useStateProps', () => { }, }, }, + "lensEmbeddableOutput$": undefined, "onBreakdownFieldChange": [Function], "onChartHiddenChange": [Function], "onChartLoad": [Function], diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts index 2daa90eddf26b..ecc0d7d863d20 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts @@ -14,7 +14,9 @@ import { Query, } from '@kbn/es-query'; import type { RequestAdapter } from '@kbn/inspector-plugin/public'; +import { LensEmbeddableOutput } from '@kbn/lens-plugin/public'; import { useCallback, useEffect, useMemo } from 'react'; +import { Observable } from 'rxjs'; import { UnifiedHistogramChartLoadEvent, UnifiedHistogramFetchStatus } from '../../types'; import type { UnifiedHistogramStateService } from '../services/state_service'; import { @@ -24,6 +26,7 @@ import { totalHitsResultSelector, totalHitsStatusSelector, lensAdaptersSelector, + lensEmbeddableOutputSelector$, } from '../utils/state_selectors'; import { useStateSelector } from '../utils/use_state_selector'; @@ -46,6 +49,10 @@ export const useStateProps = ({ const totalHitsResult = useStateSelector(stateService?.state$, totalHitsResultSelector); const totalHitsStatus = useStateSelector(stateService?.state$, totalHitsStatusSelector); const lensAdapters = useStateSelector(stateService?.state$, lensAdaptersSelector); + const lensEmbeddableOutput$ = useStateSelector( + stateService?.state$, + lensEmbeddableOutputSelector$ + ); /** * Contexts */ @@ -137,10 +144,14 @@ export const useStateProps = ({ ); const onChartLoad = useCallback( - (event: UnifiedHistogramChartLoadEvent) => { + ( + event: UnifiedHistogramChartLoadEvent, + embeddablelensEmbeddableOutput$?: Observable + ) => { // We need to store the Lens request adapter in order to inspect its requests stateService?.setLensRequestAdapter(event.adapters.requests); stateService?.setLensAdapters(event.adapters); + stateService?.setlensEmbeddableOutput$(embeddablelensEmbeddableOutput$); }, [stateService] ); @@ -177,6 +188,7 @@ export const useStateProps = ({ request, isPlainRecord, lensAdapters, + lensEmbeddableOutput$, onTopPanelHeightChange, onTimeIntervalChange, onTotalHitsChange, diff --git a/src/plugins/unified_histogram/public/container/services/state_service.test.ts b/src/plugins/unified_histogram/public/container/services/state_service.test.ts index 82daf76fc58c5..ee0905da937fd 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.test.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.test.ts @@ -139,6 +139,9 @@ describe('UnifiedHistogramStateService', () => { stateService.setLensAdapters(undefined); newState = { ...newState, lensAdapters: undefined }; expect(state).toEqual(newState); + stateService.setlensEmbeddableOutput$(undefined); + newState = { ...newState, lensEmbeddableOutput$: undefined }; + expect(state).toEqual(newState); stateService.setTotalHits({ totalHitsStatus: UnifiedHistogramFetchStatus.complete, totalHitsResult: 100, diff --git a/src/plugins/unified_histogram/public/container/services/state_service.ts b/src/plugins/unified_histogram/public/container/services/state_service.ts index 655918036e7b9..6c2ae35741127 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.ts @@ -7,7 +7,7 @@ */ import type { RequestAdapter } from '@kbn/inspector-plugin/common'; -import type { Suggestion } from '@kbn/lens-plugin/public'; +import type { LensEmbeddableOutput, Suggestion } from '@kbn/lens-plugin/public'; import { BehaviorSubject, Observable } from 'rxjs'; import { UnifiedHistogramFetchStatus } from '../..'; import type { UnifiedHistogramServices, UnifiedHistogramChartLoadEvent } from '../../types'; @@ -44,6 +44,10 @@ export interface UnifiedHistogramState { * The current Lens adapters */ lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; + /** + * Lens embeddable output observable + */ + lensEmbeddableOutput$?: Observable; /** * The current time interval of the chart */ @@ -116,6 +120,9 @@ export interface UnifiedHistogramStateService { * Sets the current Lens adapters */ setLensAdapters: (lensAdapters: UnifiedHistogramChartLoadEvent['adapters'] | undefined) => void; + setlensEmbeddableOutput$: ( + lensEmbeddableOutput$: Observable | undefined + ) => void; /** * Sets the current total hits status and result */ @@ -201,6 +208,11 @@ export const createStateService = ( setLensAdapters: (lensAdapters: UnifiedHistogramChartLoadEvent['adapters'] | undefined) => { updateState({ lensAdapters }); }, + setlensEmbeddableOutput$: ( + lensEmbeddableOutput$: Observable | undefined + ) => { + updateState({ lensEmbeddableOutput$ }); + }, setTotalHits: (totalHits: { totalHitsStatus: UnifiedHistogramFetchStatus; diff --git a/src/plugins/unified_histogram/public/container/utils/state_selectors.ts b/src/plugins/unified_histogram/public/container/utils/state_selectors.ts index 9b91cf472cdd8..f0707cdbe747e 100644 --- a/src/plugins/unified_histogram/public/container/utils/state_selectors.ts +++ b/src/plugins/unified_histogram/public/container/utils/state_selectors.ts @@ -16,3 +16,5 @@ export const totalHitsResultSelector = (state: UnifiedHistogramState) => state.t export const totalHitsStatusSelector = (state: UnifiedHistogramState) => state.totalHitsStatus; export const currentSuggestionSelector = (state: UnifiedHistogramState) => state.currentSuggestion; export const lensAdaptersSelector = (state: UnifiedHistogramState) => state.lensAdapters; +export const lensEmbeddableOutputSelector$ = (state: UnifiedHistogramState) => + state.lensEmbeddableOutput$; diff --git a/src/plugins/unified_histogram/public/layout/layout.tsx b/src/plugins/unified_histogram/public/layout/layout.tsx index 99bd5196ed3d5..f8a14a4d1391f 100644 --- a/src/plugins/unified_histogram/public/layout/layout.tsx +++ b/src/plugins/unified_histogram/public/layout/layout.tsx @@ -9,6 +9,7 @@ import { EuiSpacer, useEuiTheme, useIsWithinBreakpoints } from '@elastic/eui'; import { PropsWithChildren, ReactElement, RefObject } from 'react'; import React, { useMemo } from 'react'; +import { Observable } from 'rxjs'; import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal'; import { css } from '@emotion/css'; import type { DatatableColumn } from '@kbn/expressions-plugin/common'; @@ -16,6 +17,7 @@ import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import type { EmbeddableComponentProps, LensEmbeddableInput, + LensEmbeddableOutput, LensSuggestionsApi, Suggestion, } from '@kbn/lens-plugin/public'; @@ -84,6 +86,7 @@ export interface UnifiedHistogramLayoutProps extends PropsWithChildren */ hits?: UnifiedHistogramHitsContext; lensAdapters?: UnifiedHistogramChartLoadEvent['adapters']; + lensEmbeddableOutput$?: Observable; /** * Context object for the chart -- leave undefined to hide the chart */ @@ -181,6 +184,7 @@ export const UnifiedHistogramLayout = ({ request, hits, lensAdapters, + lensEmbeddableOutput$, chart: originalChart, breakdown, resizeRef, @@ -290,6 +294,7 @@ export const UnifiedHistogramLayout = ({ onFilter={onFilter} onBrushEnd={onBrushEnd} lensAdapters={lensAdapters} + lensEmbeddableOutput$={lensEmbeddableOutput$} isOnHistogramMode={isOnHistogramMode} withDefaultActions={withDefaultActions} /> diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 50754d8fc2d6d..743cf4710f531 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo, useCallback, useRef, useEffect, useState } from 'react'; +import React, { useMemo, useCallback, useRef, useEffect } from 'react'; import { EuiButtonEmpty, EuiButton, @@ -54,18 +54,6 @@ export interface EditConfigPanelProps { enablesResetButton?: boolean; } -const computeActiveData = (layers: string[], lensAdapters?: LensInspector['adapters']) => { - const dataTable: Record = {}; - const tables = lensAdapters?.tables?.tables as Record; - const [table] = Object.values(tables || {}); - layers.forEach((layer) => { - if (table) { - dataTable[layer] = table; - } - }); - return dataTable; -}; - export function LensEditConfigurationFlyout({ attributes, coreStart, @@ -82,24 +70,28 @@ export function LensEditConfigurationFlyout({ lensAdapters, enablesResetButton, }: EditConfigPanelProps) { - const [activeData, setActiveData] = useState>({}); const previousAttributes = useRef(attributes); const datasourceState = attributes.state.datasourceStates[datasourceId]; const activeVisualization = visualizationMap[attributes.visualizationType]; const activeDatasource = datasourceMap[datasourceId]; const { euiTheme } = useEuiTheme(); const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); - + const activeData: Record = useMemo(() => { + return {}; + }, []); useEffect(() => { - const layers = activeDatasource.getLayers(datasourceState); const s = output$?.subscribe(() => { - setActiveData(computeActiveData(layers, lensAdapters)); + const layers = activeDatasource.getLayers(datasourceState); + const adaptersTables = lensAdapters?.tables?.tables as Record; + const [table] = Object.values(adaptersTables || {}); + layers.forEach((layer) => { + if (table) { + activeData[layer] = table; + } + }); }); - if (!output$) { - setActiveData(computeActiveData(layers, lensAdapters)); - } return () => s?.unsubscribe(); - }, [activeDatasource, lensAdapters, datasourceState, output$]); + }, [activeDatasource, lensAdapters, datasourceState, output$, activeData]); const attributesChanged: boolean = useMemo(() => { const attrs = previousAttributes.current; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 5913798676c1e..d1c1ccd04ca5a 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -7,6 +7,7 @@ import { partition, uniqBy } from 'lodash'; import React from 'react'; +import type { Observable } from 'rxjs'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { render, unmountComponentAtNode } from 'react-dom'; @@ -163,7 +164,11 @@ interface LensBaseEmbeddableInput extends EmbeddableInput { className?: string; noPadding?: boolean; onBrushEnd?: (data: Simplify) => void; - onLoad?: (isLoading: boolean, adapters?: Partial) => void; + onLoad?: ( + isLoading: boolean, + adapters?: Partial, + output$?: Observable + ) => void; onFilter?: ( data: Simplify<(ClickTriggerEvent['data'] | MultiClickTriggerEvent['data']) & PreventableEvent> ) => void; @@ -901,7 +906,7 @@ export class Embeddable private updateActiveData: ExpressionWrapperProps['onData$'] = (data, adapters) => { if (this.input.onLoad) { // once onData$ is get's called from expression renderer, loading becomes false - this.input.onLoad(false, adapters); + this.input.onLoad(false, adapters, this.getOutput$()); } const { type, error } = data as { type: string; error: ErrorLike }; diff --git a/x-pack/plugins/lens/public/index.ts b/x-pack/plugins/lens/public/index.ts index 65d5ca12df094..02d3ba490b7a1 100644 --- a/x-pack/plugins/lens/public/index.ts +++ b/x-pack/plugins/lens/public/index.ts @@ -104,7 +104,12 @@ export type { ReferenceLineLayerConfig, } from '@kbn/expression-xy-plugin/common'; -export type { LensEmbeddableInput, LensSavedObjectAttributes, Embeddable } from './embeddable'; +export type { + LensEmbeddableInput, + LensSavedObjectAttributes, + Embeddable, + LensEmbeddableOutput, +} from './embeddable'; export type { ChartInfo } from './chart_info_api'; From 9bd9cdd0a0f914d161bf338dc9315e402fd0d48b Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 14 Sep 2023 10:33:35 +0300 Subject: [PATCH 15/44] Enable reset button in Discover --- .../get_edit_lens_configuration.tsx | 4 - .../lens_configuration_flyout.tsx | 80 +++++++------------ .../lens/public/embeddable/embeddable.tsx | 1 - 3 files changed, 30 insertions(+), 55 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index fc4150ed84138..8eafbd37eaf6d 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -59,8 +59,6 @@ export interface EditLensConfigurationProps { * (saved in the library) */ savedObjectId?: string; - /** When set to true it enables resetting to the previous state */ - enablesResetButton?: boolean; } function LoadingSpinnerWithOverlay() { @@ -123,7 +121,6 @@ export async function getEditLensConfiguration( output$, lensAdapters, updateByRefInput, - enablesResetButton, }: EditLensConfigurationProps) => { if (!lensServices || !datasourceMap || !visualizationMap) { return ; @@ -208,7 +205,6 @@ export async function getEditLensConfiguration( saveByRef, savedObjectId, updateByRefInput, - enablesResetButton, }; return getWrapper( diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 743cf4710f531..c0b38dc50deac 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -51,7 +51,6 @@ export interface EditConfigPanelProps { datasourceId: 'formBased' | 'textBased'; saveByRef?: (attrs: Document) => void; updateByRefInput?: (soId: string) => void; - enablesResetButton?: boolean; } export function LensEditConfigurationFlyout({ @@ -68,7 +67,6 @@ export function LensEditConfigurationFlyout({ updateByRefInput, output$, lensAdapters, - enablesResetButton, }: EditConfigPanelProps) { const previousAttributes = useRef(attributes); const datasourceState = attributes.state.datasourceStates[datasourceId]; @@ -237,54 +235,36 @@ export function LensEditConfigurationFlyout({ - {Boolean(enablesResetButton) && ( - - - - - - - - - - - - - )} - {!Boolean(enablesResetButton) && ( - - - - )} + + + + + + + + + + + + ); diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index d1c1ccd04ca5a..1e8caf6c4bccc 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -813,7 +813,6 @@ export class Embeddable updatePanelState={this.updateVisualization.bind(this)} datasourceId={datasourceId} lensAdapters={this.lensInspector.adapters} - enablesResetButton={true} output$={this.getOutput$()} panelId={this.id} savedObjectId={this.savedVis?.savedObjectId} From 02cfa2fc6be0c4c041a3dd7c2547bf810bb691a3 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 14 Sep 2023 13:28:36 +0300 Subject: [PATCH 16/44] Navigate to the editor --- .../get_edit_lens_configuration.tsx | 45 +++------- .../lens_configuration_flyout.tsx | 86 ++++++++++++++++++- .../lens/public/embeddable/embeddable.tsx | 28 +++++- 3 files changed, 121 insertions(+), 38 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index 8eafbd37eaf6d..0095db785af06 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -11,13 +11,11 @@ import { i18n } from '@kbn/i18n'; import { Provider } from 'react-redux'; import { MiddlewareAPI, Dispatch, Action } from '@reduxjs/toolkit'; import { css } from '@emotion/react'; -import type { Observable } from 'rxjs'; import type { CoreStart } from '@kbn/core/public'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { isEqual } from 'lodash'; import { RootDragDropProvider } from '@kbn/dom-drag-drop'; import type { LensPluginStartDependencies } from '../../../plugin'; -import type { LensEmbeddableOutput } from '../../../embeddable'; import { makeConfigureStore, LensRootStore, @@ -25,42 +23,19 @@ import { initExisting, initEmpty, } from '../../../state_management'; -import type { TypedLensByValueInput } from '../../../embeddable/embeddable_component'; import { generateId } from '../../../id_generator'; import type { DatasourceMap, VisualizationMap } from '../../../types'; -import { LensEditConfigurationFlyout } from './lens_configuration_flyout'; +import { + LensEditConfigurationFlyout, + type EditConfigPanelProps, +} from './lens_configuration_flyout'; import { SavedObjectIndexStore, type Document } from '../../../persistence'; -import type { LensInspector } from '../../../lens_inspector_service'; import { DOC_TYPE } from '../../../../common/constants'; -export interface EditLensConfigurationProps { - /** The attributes of the Lens embeddable */ - attributes: TypedLensByValueInput['attributes']; - /** Callback for updating the visualization and datasources state */ - updatePanelState: (datasourceState: unknown, visualizationState: unknown) => void; - /** Lens visualizations can be either created from ESQL (textBased) or from dataviews (formBased) */ - datasourceId: 'formBased' | 'textBased'; - /** Embeddable output observable, useful for dashboard flyout */ - output$?: Observable; - /** Contains the active data, necessary for some panel configuration such as coloring */ - lensAdapters?: LensInspector['adapters']; - /** Optional callback called when updating the by reference embeddable */ - updateByRefInput?: (soId: string) => void; - /** Callback for closing the edit flyout */ - closeFlyout?: () => void; - /** Boolean used for adding a flyout wrapper */ - wrapInFlyout?: boolean; - /** Optional parameter for panel identification - * If not given, Lens generates a new one - */ - panelId?: string; - /** Optional parameter for saved object id - * Should be given if the lens embeddable is a by reference one - * (saved in the library) - */ - savedObjectId?: string; -} - +export type EditLensConfigurationProps = Omit< + EditConfigPanelProps, + 'startDependencies' | 'coreStart' | 'visualizationMap' | 'datasourceMap' | 'saveByRef' +>; function LoadingSpinnerWithOverlay() { return ( @@ -121,6 +96,8 @@ export async function getEditLensConfiguration( output$, lensAdapters, updateByRefInput, + navigateToLensEditor, + displayFlyoutHeader, }: EditLensConfigurationProps) => { if (!lensServices || !datasourceMap || !visualizationMap) { return ; @@ -205,6 +182,8 @@ export async function getEditLensConfiguration( saveByRef, savedObjectId, updateByRefInput, + navigateToLensEditor, + displayFlyoutHeader, }; return getWrapper( diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index c0b38dc50deac..2c0c636347b7a 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -11,6 +11,11 @@ import { EuiButton, EuiFlyoutBody, EuiFlyoutFooter, + EuiFlyoutHeader, + EuiTitle, + EuiLink, + EuiIcon, + EuiToolTip, EuiSpacer, EuiFlexGroup, EuiFlexItem, @@ -38,19 +43,41 @@ import { extractReferencesFromState } from '../../../utils'; import type { Document } from '../../../persistence'; export interface EditConfigPanelProps { - attributes: TypedLensByValueInput['attributes']; - updatePanelState: (datasourceState: unknown, visualizationState: unknown) => void; coreStart: CoreStart; startDependencies: LensPluginStartDependencies; visualizationMap: VisualizationMap; datasourceMap: DatasourceMap; + /** The attributes of the Lens embeddable */ + attributes: TypedLensByValueInput['attributes']; + /** Callback for updating the visualization and datasources state */ + updatePanelState: (datasourceState: unknown, visualizationState: unknown) => void; + /** Lens visualizations can be either created from ESQL (textBased) or from dataviews (formBased) */ + datasourceId: 'formBased' | 'textBased'; + /** Embeddable output observable, useful for dashboard flyout */ output$?: Observable; + /** Contains the active data, necessary for some panel configuration such as coloring */ lensAdapters?: LensInspector['adapters']; + /** Optional callback called when updating the by reference embeddable */ + updateByRefInput?: (soId: string) => void; + /** Callback for closing the edit flyout */ closeFlyout?: () => void; + /** Boolean used for adding a flyout wrapper */ + wrapInFlyout?: boolean; + /** Optional parameter for panel identification + * If not given, Lens generates a new one + */ + panelId?: string; + /** Optional parameter for saved object id + * Should be given if the lens embeddable is a by reference one + * (saved in the library) + */ savedObjectId?: string; - datasourceId: 'formBased' | 'textBased'; + /** Callback for saving the embeddable as a SO */ saveByRef?: (attrs: Document) => void; - updateByRefInput?: (soId: string) => void; + /** Optional callback for navigation from the header of the flyout */ + navigateToLensEditor?: () => void; + /** If set to true it displays a header on the flyout */ + displayFlyoutHeader?: boolean; } export function LensEditConfigurationFlyout({ @@ -67,6 +94,8 @@ export function LensEditConfigurationFlyout({ updateByRefInput, output$, lensAdapters, + navigateToLensEditor, + displayFlyoutHeader, }: EditConfigPanelProps) { const previousAttributes = useRef(attributes); const datasourceState = attributes.state.datasourceStates[datasourceId]; @@ -193,6 +222,55 @@ export function LensEditConfigurationFlyout({ }; return ( <> + {displayFlyoutHeader && ( + + + + + + +

+ {i18n.translate('xpack.lens.config.editLabel', { + defaultMessage: 'Edit visualization', + })} +

+
+
+ + + + + +
+
+ {navigateToLensEditor && ( + + {/* + + + + + + {i18n.translate('xpack.lens.config.editLinkLabel', { + defaultMessage: 'Edit in Lens', + })} + + + */} + + {i18n.translate('xpack.lens.config.editLinkLabel', { + defaultMessage: 'Edit in Lens', + })} + + + )} +
+
+ )} ); } From 08d704c5d4649361147b91978e2260aa5eea0c49 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 18 Sep 2023 08:59:41 +0300 Subject: [PATCH 17/44] fix translation --- .../shared/edit_on_the_fly/lens_configuration_flyout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 2c0c636347b7a..a574d9a9e6a42 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -230,7 +230,7 @@ export function LensEditConfigurationFlyout({

- {i18n.translate('xpack.lens.config.editLabel', { + {i18n.translate('xpack.lens.config.editVisualizationLabel', { defaultMessage: 'Edit visualization', })}

From be818f053f93550151a2bdc98105ac5812668afc Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 18 Sep 2023 11:41:16 +0300 Subject: [PATCH 18/44] Fix content management change and userMessages, add indexpattern change support --- .../get_edit_lens_configuration.tsx | 2 +- .../lens_configuration_flyout.tsx | 42 +++++++++++++------ .../lens/public/embeddable/embeddable.tsx | 34 +++++++++------ 3 files changed, 52 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index 0095db785af06..63f1ec26413b6 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -108,7 +108,7 @@ export async function getEditLensConfiguration( */ const saveByRef = useCallback( async (attrs: Document) => { - const savedObjectStore = new SavedObjectIndexStore(lensServices.contentManagement.client); + const savedObjectStore = new SavedObjectIndexStore(lensServices.contentManagement); await savedObjectStore.save({ ...attrs, savedObjectId, diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index a574d9a9e6a42..56699550d0e16 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -31,7 +31,14 @@ import { css } from '@emotion/react'; import type { CoreStart } from '@kbn/core/public'; import type { Datatable } from '@kbn/expressions-plugin/public'; import type { LensPluginStartDependencies } from '../../../plugin'; -import { useLensSelector, selectFramePublicAPI } from '../../../state_management'; +import { + useLensSelector, + selectFramePublicAPI, + useLensDispatch, + updateIndexPatterns, + applyChanges, +} from '../../../state_management'; +import { replaceIndexpattern } from '../../../state_management/lens_slice'; import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame/workspace_panel'; import type { DatasourceMap, VisualizationMap } from '../../../types'; @@ -41,6 +48,7 @@ import type { LensInspector } from '../../../lens_inspector_service'; import { ConfigPanelWrapper } from '../../../editor_frame_service/editor_frame/config_panel/config_panel'; import { extractReferencesFromState } from '../../../utils'; import type { Document } from '../../../persistence'; +import { createIndexPatternService } from '../../../data_views_service/service'; export interface EditConfigPanelProps { coreStart: CoreStart; @@ -103,6 +111,7 @@ export function LensEditConfigurationFlyout({ const activeDatasource = datasourceMap[datasourceId]; const { euiTheme } = useEuiTheme(); const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); + const dispatch = useLensDispatch(); const activeData: Record = useMemo(() => { return {}; }, []); @@ -199,6 +208,24 @@ export function LensEditConfigurationFlyout({ datasourceMap, ]); + const indexPatternService = useMemo( + () => + createIndexPatternService({ + dataViews: startDependencies.dataViews, + uiActions: startDependencies.uiActions, + core: coreStart, + updateIndexPatterns: (newIndexPatternsState, options) => { + dispatch(updateIndexPatterns(newIndexPatternsState)); + dispatch(applyChanges()); + }, + replaceIndexPattern: (newIndexPattern, oldId, options) => { + dispatch(replaceIndexpattern({ newIndexPattern, oldId })); + dispatch(applyChanges()); + }, + }), + [coreStart, dispatch, startDependencies.dataViews, startDependencies.uiActions] + ); + const framePublicAPI = useLensSelector((state) => { const newState = { ...state, @@ -219,6 +246,7 @@ export function LensEditConfigurationFlyout({ dataViews: startDependencies.dataViews, uiActions: startDependencies.uiActions, hideLayerHeader: datasourceId === 'textBased', + indexPatternService, }; return ( <> @@ -249,18 +277,6 @@ export function LensEditConfigurationFlyout({
{navigateToLensEditor && ( - {/* - - - - - - {i18n.translate('xpack.lens.config.editLinkLabel', { - defaultMessage: 'Edit in Lens', - })} - - - */} {i18n.translate('xpack.lens.config.editLinkLabel', { defaultMessage: 'Edit in Lens', diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 38c052770cce7..37499750e7595 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -611,21 +611,29 @@ export class Embeddable private _userMessages: UserMessage[] = []; // loads all available user messages - private loadUserMessages() { + private loadUserMessages(attrs?: Document) { const userMessages: UserMessage[] = []; + const viz = attrs ?? this.savedVis; + const activeVisualizationState = attrs + ? attrs.state.visualization + : this.activeVisualizationState; + + const activeDatasourceState = attrs + ? attrs.state.datasourceStates[this.activeDatasourceId ?? 'formBased'] + : this.activeDatasourceState; userMessages.push( ...getApplicationUserMessages({ - visualizationType: this.savedVis?.visualizationType, + visualizationType: viz?.visualizationType, visualization: { - state: this.activeVisualizationState, + state: activeVisualizationState, activeId: this.activeVisualizationId, }, visualizationMap: this.deps.visualizationMap, activeDatasource: this.activeDatasource, activeDatasourceState: { - isLoading: !this.activeDatasourceState, - state: this.activeDatasourceState, + isLoading: !activeDatasourceState, + state: activeDatasourceState, }, dataViews: { indexPatterns: this.indexPatterns, @@ -635,7 +643,7 @@ export class Embeddable }) ); - if (!this.savedVis) { + if (!viz) { return userMessages; } const mergedSearchContext = this.getMergedSearchContext(); @@ -648,14 +656,14 @@ export class Embeddable datasourceLayers: getDatasourceLayers( { [this.activeDatasourceId!]: { - isLoading: !this.activeDatasourceState, - state: this.activeDatasourceState, + isLoading: !activeDatasourceState, + state: activeDatasourceState, }, }, this.deps.datasourceMap, this.indexPatterns ), - query: this.savedVis.state.query, + query: viz.state.query, filters: mergedSearchContext.filters ?? [], dateRange: { fromDate: mergedSearchContext.timeRange?.from ?? '', @@ -665,15 +673,15 @@ export class Embeddable }; userMessages.push( - ...(this.activeDatasource?.getUserMessages(this.activeDatasourceState, { + ...(this.activeDatasource?.getUserMessages(activeDatasourceState, { setState: () => {}, frame: frameDatasourceAPI, visualizationInfo: this.activeVisualization?.getVisualizationInfo?.( - this.activeVisualizationState, + activeVisualizationState, frameDatasourceAPI ), }) ?? []), - ...(this.activeVisualization?.getUserMessages?.(this.activeVisualizationState, { + ...(this.activeVisualization?.getUserMessages?.(activeVisualizationState, { frame: frameDatasourceAPI, }) ?? []) ); @@ -786,6 +794,8 @@ export class Embeddable * Here we are converting the by reference panels to by value when user is inline editing */ this.updateInput({ attributes: attrs, savedObjectId: undefined }); + // should load again the user messages, otherwise the embeddable state is stuck in an error state + this.loadUserMessages(attrs); } } From 02a888438c2c78e7a9c260a6c564c4df2fc78422 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 18 Sep 2023 12:07:53 +0300 Subject: [PATCH 19/44] Fixes broken jest tests --- .../public/chart/histogram.test.tsx | 10 +++++----- .../lens_configuration_flyout.test.tsx | 2 +- .../lens_configuration_flyout.tsx | 6 ++---- .../lens/public/embeddable/embeddable.test.tsx | 4 ++-- .../open_lens_config/action.test.tsx | 17 +++++++++++++++++ .../test/functional/page_objects/lens_page.ts | 2 +- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/plugins/unified_histogram/public/chart/histogram.test.tsx b/src/plugins/unified_histogram/public/chart/histogram.test.tsx index 78f06687a0e7e..375528230323d 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.test.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.test.tsx @@ -171,7 +171,7 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.loading, undefined ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters: {} }); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters: {} }, undefined); expect(buildBucketInterval.buildBucketInterval).not.toHaveBeenCalled(); expect(useTimeRange.useTimeRange).toHaveBeenLastCalledWith( expect.objectContaining({ bucketInterval: undefined }) @@ -183,7 +183,7 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.complete, 100 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, undefined); expect(buildBucketInterval.buildBucketInterval).toHaveBeenCalled(); expect(useTimeRange.useTimeRange).toHaveBeenLastCalledWith( expect.objectContaining({ bucketInterval: mockBucketInterval }) @@ -236,7 +236,7 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.complete, 100 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, undefined); }); it('should execute onLoad correctly for textbased language and no Lens suggestions', async () => { @@ -272,7 +272,7 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.complete, 20 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, undefined); }); it('should execute onLoad correctly for textbased language and Lens suggestions', async () => { @@ -308,6 +308,6 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.complete, 2 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, undefined); }); }); diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx index fa2cebc1e43b0..5d4d30aaac640 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx @@ -131,7 +131,7 @@ describe('LensEditConfigurationFlyout', () => { }; const { instance } = await prepareAndMountComponent(newProps); expect(instance.find(EuiFlyoutBody).exists()).toBe(true); - instance.find('[data-test-subj="collapseFlyoutButton"]').at(1).simulate('click'); + instance.find('[data-test-subj="cancelFlyoutButton"]').at(1).simulate('click'); expect(closeFlyoutSpy).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 56699550d0e16..5854f71a60264 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -36,7 +36,6 @@ import { selectFramePublicAPI, useLensDispatch, updateIndexPatterns, - applyChanges, } from '../../../state_management'; import { replaceIndexpattern } from '../../../state_management/lens_slice'; import { VisualizationToolbar } from '../../../editor_frame_service/editor_frame/workspace_panel'; @@ -216,11 +215,9 @@ export function LensEditConfigurationFlyout({ core: coreStart, updateIndexPatterns: (newIndexPatternsState, options) => { dispatch(updateIndexPatterns(newIndexPatternsState)); - dispatch(applyChanges()); }, replaceIndexPattern: (newIndexPattern, oldId, options) => { dispatch(replaceIndexpattern({ newIndexPattern, oldId })); - dispatch(applyChanges()); }, }), [coreStart, dispatch, startDependencies.dataViews, startDependencies.uiActions] @@ -337,7 +334,7 @@ export function LensEditConfigurationFlyout({ aria-label={i18n.translate('xpack.lens.config.cancelFlyoutAriaLabel', { defaultMessage: 'Cancel applied changes', })} - data-test-subj="collapseFlyoutButton" + data-test-subj="cancelFlyoutButton" > @@ -351,6 +348,7 @@ export function LensEditConfigurationFlyout({ })} iconType="check" isDisabled={!attributesChanged} + data-test-subj="applyFlyoutButton" > { // loading should become false expect(onLoad).toHaveBeenCalledTimes(2); - expect(onLoad).toHaveBeenNthCalledWith(2, false, adapters); + expect(onLoad).toHaveBeenNthCalledWith(2, false, adapters, embeddable.getOutput$()); expect(expressionRenderer).toHaveBeenCalledTimes(1); @@ -698,7 +698,7 @@ describe('embeddable', () => { // loading should again become false expect(onLoad).toHaveBeenCalledTimes(4); - expect(onLoad).toHaveBeenNthCalledWith(4, false, adapters); + expect(onLoad).toHaveBeenNthCalledWith(4, false, adapters, embeddable.getOutput$()); }); it('should call onFilter event on filter call ', async () => { diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx index 42c1938cc62cb..530aa12c659d7 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx @@ -24,6 +24,11 @@ describe('open config panel action', () => { const embeddable = { type: 'NOT_LENS', isTextBasedLanguage: () => true, + getInput: () => { + return { + viewMode: 'edit', + }; + }, } as unknown as IEmbeddable; const configurablePanelAction = new ConfigureInLensPanelAction( mockStartDependencies, @@ -42,6 +47,12 @@ describe('open config panel action', () => { const embeddable = { type: DOC_TYPE, isTextBasedLanguage: () => true, + getInput: () => { + return { + viewMode: 'edit', + }; + }, + getIsEditable: () => true, } as unknown as IEmbeddable; const configurablePanelAction = new ConfigureInLensPanelAction( mockStartDependencies, @@ -61,6 +72,12 @@ describe('open config panel action', () => { const embeddable = { type: DOC_TYPE, isTextBasedLanguage: () => true, + getInput: () => { + return { + viewMode: 'edit', + }; + }, + getIsEditable: () => true, openConfingPanel: jest.fn().mockResolvedValue(Lens Config Panel Component), getRoot: () => { return { diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 6880a3ab46ff5..3c0097bdedb8c 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -1472,7 +1472,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont } if (!opts.keepOpen) { - await testSubjects.click('collapseFlyoutButton'); + await testSubjects.click('applyFlyoutButton'); } }, From 0e4b64b46da5dc2e3cc09b114e684431ff4d53a9 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 18 Sep 2023 14:29:16 +0300 Subject: [PATCH 20/44] Remove edit action and fix FTs --- .../services/dashboard/panel_actions.ts | 45 ++++++++++++++++--- .../lens_configuration_flyout.tsx | 2 +- .../lens/public/embeddable/embeddable.tsx | 32 ++++++++++--- .../apps/canvas/embeddables/lens.ts | 2 +- .../lens_migration_smoke_test.ts | 2 +- 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/test/functional/services/dashboard/panel_actions.ts b/test/functional/services/dashboard/panel_actions.ts index f81ddf9d69bb5..ae451525d9a1a 100644 --- a/test/functional/services/dashboard/panel_actions.ts +++ b/test/functional/services/dashboard/panel_actions.ts @@ -11,6 +11,8 @@ import { FtrService } from '../../ftr_provider_context'; const REMOVE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-deletePanel'; const EDIT_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-editPanel'; +const INLINE_EDIT_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-ACTION_CONFIGURE_IN_LENS'; +const EDIT_IN_LENS_EDITOR_DATA_TEST_SUBJ = 'navigateToLensEditorLink'; const REPLACE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-replacePanel'; const CLONE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-clonePanel'; const TOGGLE_EXPAND_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-togglePanel'; @@ -87,16 +89,41 @@ export class DashboardPanelActionsService extends FtrService { await this.clickContextMenuMoreItem(); } + private async navigateToEditorFromFlyout() { + await this.testSubjects.clickWhenNotDisabledWithoutRetry(INLINE_EDIT_PANEL_DATA_TEST_SUBJ); + await this.header.waitUntilLoadingHasFinished(); + await this.testSubjects.click(EDIT_IN_LENS_EDITOR_DATA_TEST_SUBJ); + const isConfirmModalVisible = await this.testSubjects.exists('confirmModalConfirmButton'); + if (isConfirmModalVisible) { + await this.testSubjects.click('confirmModalConfirmButton', 20000); + } + } + + /** The dashboard/canvas panels can be either edited on their editor or inline. + * The inline editing panels allow the navigation to the editor after the flyout opens + */ async clickEdit() { this.log.debug('clickEdit'); await this.expectContextMenuToBeOpen(); const isActionVisible = await this.testSubjects.exists(EDIT_PANEL_DATA_TEST_SUBJ); - if (!isActionVisible) await this.clickContextMenuMoreItem(); - await this.testSubjects.clickWhenNotDisabledWithoutRetry(EDIT_PANEL_DATA_TEST_SUBJ); + const isInlineEditingActionVisible = await this.testSubjects.exists( + INLINE_EDIT_PANEL_DATA_TEST_SUBJ + ); + if (!isActionVisible && !isInlineEditingActionVisible) await this.clickContextMenuMoreItem(); + // navigate to the editor + if (await this.testSubjects.exists(EDIT_PANEL_DATA_TEST_SUBJ)) { + await this.testSubjects.clickWhenNotDisabledWithoutRetry(EDIT_PANEL_DATA_TEST_SUBJ); + // open the flyout and then navigate to the editor + } else { + await this.navigateToEditorFromFlyout(); + } await this.header.waitUntilLoadingHasFinished(); await this.common.waitForTopNavToBeVisible(); } + /** The dashboard/canvas panels can be either edited on their editor or inline. + * The inline editing panels allow the navigation to the editor after the flyout opens + */ async editPanelByTitle(title?: string) { this.log.debug(`editPanelByTitle(${title})`); if (title) { @@ -105,7 +132,11 @@ export class DashboardPanelActionsService extends FtrService { } else { await this.openContextMenu(); } - await this.testSubjects.clickWhenNotDisabledWithoutRetry(EDIT_PANEL_DATA_TEST_SUBJ); + if (await this.testSubjects.exists(EDIT_PANEL_DATA_TEST_SUBJ)) { + await this.testSubjects.clickWhenNotDisabledWithoutRetry(EDIT_PANEL_DATA_TEST_SUBJ); + } else { + await this.navigateToEditorFromFlyout(); + } } async clickExpandPanelToggle() { @@ -266,9 +297,13 @@ export class DashboardPanelActionsService extends FtrService { await this.expectExistsPanelAction(REMOVE_PANEL_DATA_TEST_SUBJ); } - async expectExistsEditPanelAction(title?: string) { + async expectExistsEditPanelAction(title?: string, allowsInlineEditing?: boolean) { this.log.debug('expectExistsEditPanelAction'); - await this.expectExistsPanelAction(EDIT_PANEL_DATA_TEST_SUBJ, title); + let testSubj = EDIT_PANEL_DATA_TEST_SUBJ; + if (allowsInlineEditing) { + testSubj = INLINE_EDIT_PANEL_DATA_TEST_SUBJ; + } + await this.expectExistsPanelAction(testSubj, title); } async expectExistsReplacePanelAction() { diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 5854f71a60264..53b0027fef361 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -274,7 +274,7 @@ export function LensEditConfigurationFlyout({ {navigateToLensEditor && ( - + {i18n.translate('xpack.lens.config.editLinkLabel', { defaultMessage: 'Edit in Lens', })} diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 37499750e7595..a01d2fcf2c7ec 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -754,6 +754,10 @@ export class Embeddable return String(language).toUpperCase(); } + /** + * Gets the Lens embeddable's datasource and visualization states + * updates the embeddable input + */ async updateVisualization(datasourceState: unknown, visualizationState: unknown) { const viz = this.savedVis; const activeDatasourceId = (this.activeDatasourceId ?? @@ -794,16 +798,31 @@ export class Embeddable * Here we are converting the by reference panels to by value when user is inline editing */ this.updateInput({ attributes: attrs, savedObjectId: undefined }); - // should load again the user messages, otherwise the embeddable state is stuck in an error state + /** + * Should load again the user messages, + * otherwise the embeddable state is stuck in an error state + */ this.loadUserMessages(attrs); } } + /** + * Callback which allows the navigation to the editor. + * Used for the Edit in Lens link inside the inline editing flyout. + */ private async navigateToLensEditor() { + const executionContext = this.getExecutionContext(); + /** + * The origininating app variable is very important for the Save and Return button + * of the editor to work properly. + * The best way to get it dynamically is from the execution context but for the dashboard + * it needs to be pluralized + */ const transferState = { - // ToDo: this must come from the consumers of the embeddable - // Change it to be a prop when we add support for inline editing outside from the dashboard - originatingApp: 'dashboards', + originatingApp: + executionContext?.type === 'dashboard' + ? 'dashboards' + : executionContext?.type ?? 'dashboards', valueInput: this.getExplicitInput(), embeddableId: this.id, searchSessionId: this.getInput().searchSessionId, @@ -1469,7 +1488,10 @@ export class Embeddable this.updateOutput({ defaultTitle: this.savedVis.title, defaultDescription: this.savedVis.description, - editable: this.getIsEditable() && !this.isTextBasedLanguage(), + /** lens visualizations allow inline editing action + * navigation to the editor is allowed through the flyout + */ + editable: false, title, description, editPath: getEditPath(savedObjectId), diff --git a/x-pack/test/functional/apps/canvas/embeddables/lens.ts b/x-pack/test/functional/apps/canvas/embeddables/lens.ts index de7a2eb753204..5f9c6ec445b97 100644 --- a/x-pack/test/functional/apps/canvas/embeddables/lens.ts +++ b/x-pack/test/functional/apps/canvas/embeddables/lens.ts @@ -83,7 +83,7 @@ export default function canvasLensTest({ getService, getPageObjects }: FtrProvid const panelHeader = await testSubjects.find('embeddablePanelHeading-'); await dashboardPanelActions.openContextMenu(panelHeader); await dashboardPanelActions.clickEdit(); - await await PageObjects.lens.saveAndReturn(); + await PageObjects.lens.saveAndReturn(); await PageObjects.header.waitUntilLoadingHasFinished(); await testSubjects.exists('xyVisChart'); }); diff --git a/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/lens_migration_smoke_test.ts b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/lens_migration_smoke_test.ts index 024c045c4ff87..9c65fcf31cb06 100644 --- a/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/lens_migration_smoke_test.ts +++ b/x-pack/test/functional/apps/dashboard/group2/migration_smoke_tests/lens_migration_smoke_test.ts @@ -64,7 +64,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // All panels should be editable. This will catch cases where an error does not create an error embeddable. const panelTitles = await PageObjects.dashboard.getPanelTitles(); for (const title of panelTitles) { - await dashboardPanelActions.expectExistsEditPanelAction(title); + await dashboardPanelActions.expectExistsEditPanelAction(title, true); } }); From 22ae74e1df89b0a0fbd9e1673b6e81eb36437c5f Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 18 Sep 2023 15:29:36 +0300 Subject: [PATCH 21/44] Display clone panel --- .../panel_actions/edit_panel_action/edit_panel_action.ts | 1 + src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts | 1 + x-pack/plugins/lens/public/embeddable/embeddable.tsx | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/embeddable/public/embeddable_panel/panel_actions/edit_panel_action/edit_panel_action.ts b/src/plugins/embeddable/public/embeddable_panel/panel_actions/edit_panel_action/edit_panel_action.ts index 942830da96e60..3f465f2d37439 100644 --- a/src/plugins/embeddable/public/embeddable_panel/panel_actions/edit_panel_action/edit_panel_action.ts +++ b/src/plugins/embeddable/public/embeddable_panel/panel_actions/edit_panel_action/edit_panel_action.ts @@ -75,6 +75,7 @@ export class EditPanelAction implements Action { const canEditEmbeddable = Boolean( embeddable && embeddable.getOutput().editable && + !embeddable.getOutput().inlineEditable && (embeddable.getOutput().editUrl || (embeddable.getOutput().editApp && embeddable.getOutput().editPath) || embeddable.getOutput().editableWithExplicitInput) diff --git a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts index 4f82e21440163..9269e8c45273d 100644 --- a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts +++ b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts @@ -30,6 +30,7 @@ export interface EmbeddableOutput { title?: string; description?: string; editable?: boolean; + inlineEditable?: boolean; // Whether the embeddable can be edited inline by re-requesting the explicit input from the user editableWithExplicitInput?: boolean; savedObjectId?: string; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index a01d2fcf2c7ec..2b2306a668d2f 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -1491,7 +1491,8 @@ export class Embeddable /** lens visualizations allow inline editing action * navigation to the editor is allowed through the flyout */ - editable: false, + editable: this.getIsEditable(), + inlineEditable: true, title, description, editPath: getEditPath(savedObjectId), From c48bd2a67a2736512ab019ae6c93548bcdc0cad0 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 19 Sep 2023 08:33:00 +0300 Subject: [PATCH 22/44] fixes the bug with adding a layer with another dataview --- .../lens/public/embeddable/embeddable.tsx | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 2b2306a668d2f..3b8643b9b121c 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -61,7 +61,11 @@ import { shouldFetch$, } from '@kbn/embeddable-plugin/public'; import type { Action, UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import type { DataViewsContract, DataView } from '@kbn/data-views-plugin/public'; +import type { + DataViewsContract, + DataView, + DataViewsPublicPluginStart, +} from '@kbn/data-views-plugin/public'; import type { Capabilities, CoreStart, @@ -136,6 +140,7 @@ import type { LensPluginStartDependencies } from '../plugin'; import { EmbeddableFeatureBadge } from './embeddable_info_badges'; import { getDatasourceLayers } from '../state_management/utils'; import type { EditLensConfigurationProps } from '../app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration'; +import { convertDataViewIntoLensIndexPattern } from '../data_views_service/loader'; export type LensSavedObjectAttributes = Omit; @@ -415,6 +420,22 @@ const MessagesBadge = ({ onMount }: { onMount: (el: HTMLDivElement) => void }) = /> ); +const getIndexPatternsObject = async (attrs: Document, dataViews: DataViewsPublicPluginStart) => { + const { indexPatterns } = await getIndexPatternsObjects( + attrs?.references.map(({ id }) => id) || [], + dataViews + ); + + const indexPatternsObject = indexPatterns?.reduce( + (acc, indexPattern) => ({ + [indexPattern.id!]: convertDataViewIntoLensIndexPattern(indexPattern), + ...acc, + }), + {} + ); + return indexPatternsObject; +}; + export class Embeddable extends AbstractEmbeddable implements @@ -611,7 +632,7 @@ export class Embeddable private _userMessages: UserMessage[] = []; // loads all available user messages - private loadUserMessages(attrs?: Document) { + private async loadUserMessages(attrs?: Document) { const userMessages: UserMessage[] = []; const viz = attrs ?? this.savedVis; const activeVisualizationState = attrs @@ -622,6 +643,10 @@ export class Embeddable ? attrs.state.datasourceStates[this.activeDatasourceId ?? 'formBased'] : this.activeDatasourceState; + const indexPatterns = attrs + ? await getIndexPatternsObject(attrs, this.deps.dataViews) + : this.indexPatterns; + userMessages.push( ...getApplicationUserMessages({ visualizationType: viz?.visualizationType, @@ -636,7 +661,7 @@ export class Embeddable state: activeDatasourceState, }, dataViews: { - indexPatterns: this.indexPatterns, + indexPatterns, indexPatternRefs: this.indexPatternRefs, // TODO - are these actually used? }, core: this.deps.coreStart, @@ -802,7 +827,7 @@ export class Embeddable * Should load again the user messages, * otherwise the embeddable state is stuck in an error state */ - this.loadUserMessages(attrs); + await this.loadUserMessages(attrs); } } From 2dcc21508b5a73cea9748a798488891de2a14dc7 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 19 Sep 2023 09:48:15 +0300 Subject: [PATCH 23/44] Fix bugs concerning the indexpattern id --- .../lens_configuration_flyout.tsx | 41 +++++++++++++------ .../datasources/form_based/form_based.tsx | 20 ++++----- .../text_based/text_based_languages.tsx | 3 -- x-pack/plugins/lens/public/types.ts | 3 +- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 53b0027fef361..291c414b6e8f7 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -129,25 +129,39 @@ export function LensEditConfigurationFlyout({ }, [activeDatasource, lensAdapters, datasourceState, output$, activeData]); const attributesChanged: boolean = useMemo(() => { - const attrs = previousAttributes.current; - const prevLayers = datasourceMap[datasourceId].getCurrentLayersState?.( - attrs.state.datasourceStates[datasourceId] - ); + const previousAttrs = previousAttributes.current; + const prevDatasourceState = datasourceMap[datasourceId].injectReferencesToLayers + ? datasourceMap[datasourceId]?.injectReferencesToLayers?.( + previousAttrs.state.datasourceStates[datasourceId], + previousAttrs.references + ) + : previousAttrs.state.datasourceStates[datasourceId]; + + const currentDatasourceState = datasourceMap[datasourceId].injectReferencesToLayers + ? datasourceMap[datasourceId]?.injectReferencesToLayers?.( + datasourceStates[datasourceId].state, + attributes.references + ) + : datasourceStates[datasourceId].state; const visualizationState = visualization.state; - const datasourceLayers = datasourceMap[datasourceId].getCurrentLayersState?.( - datasourceStates[datasourceId].state - ); return ( - !isEqual(visualizationState, attrs.state.visualization) || - !isEqual(datasourceLayers, prevLayers) + !isEqual(visualizationState, previousAttrs.state.visualization) || + !isEqual(prevDatasourceState, currentDatasourceState) ); - }, [datasourceId, datasourceMap, datasourceStates, visualization.state]); + }, [attributes.references, datasourceId, datasourceMap, datasourceStates, visualization.state]); const onCancel = useCallback(() => { - const attrs = previousAttributes.current; + const previousAttrs = previousAttributes.current; + if (attributesChanged) { - updatePanelState?.(attrs.state.datasourceStates[datasourceId], attrs.state.visualization); + const currentDatasourceState = datasourceMap[datasourceId].injectReferencesToLayers + ? datasourceMap[datasourceId]?.injectReferencesToLayers?.( + previousAttrs.state.datasourceStates[datasourceId], + previousAttrs.references + ) + : previousAttrs.state.datasourceStates[datasourceId]; + updatePanelState?.(currentDatasourceState, previousAttrs.state.visualization); } if (savedObjectId) { updateByRefInput?.(savedObjectId); @@ -157,8 +171,9 @@ export function LensEditConfigurationFlyout({ attributesChanged, savedObjectId, closeFlyout, - updatePanelState, + datasourceMap, datasourceId, + updatePanelState, updateByRefInput, ]); diff --git a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx index 64913ae78a7f5..eee18d41b49a7 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx @@ -322,18 +322,6 @@ export function getFormBasedDatasource({ return Object.keys(state?.layers); }, - getCurrentLayersState(state: FormBasedPrivateState) { - const layers = { ...state?.layers }; - const updatedLayers = Object.fromEntries( - Object.entries(layers).map(([id, layer]) => { - const { indexPatternId, ...newLayer } = layer; - - return [id, newLayer]; - }) - ); - return updatedLayers; - }, - removeColumn, initializeDimension( @@ -870,6 +858,14 @@ export function getFormBasedDatasource({ getUsedDataViews: (state) => { return Object.values(state.layers).map(({ indexPatternId }) => indexPatternId); }, + injectReferencesToLayers: (state, references) => { + const layers = + references && state ? injectReferences(state, references).layers : state?.layers; + return { + ...state, + layers, + }; + }, getDatasourceInfo: async (state, references, dataViewsService) => { const layers = references ? injectReferences(state, references).layers : state.layers; diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index c4c6a9c2d9dd7..a76e2edd623b0 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -304,9 +304,6 @@ export function getTextBasedDatasource({ getLayers(state: TextBasedPrivateState) { return state && state.layers ? Object.keys(state?.layers) : []; }, - getCurrentLayersState(state: TextBasedPrivateState) { - return state?.layers; - }, isTimeBased: (state, indexPatterns) => { if (!state) return false; const { layers } = state; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index d6c8374c8088d..3f98c0e906dbe 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -340,7 +340,6 @@ export interface Datasource { getNewId: (id: string) => string ) => T; getLayers: (state: T) => string[]; - getCurrentLayersState?: (state: T) => unknown; removeColumn: (props: { prevState: T; layerId: string; @@ -508,6 +507,8 @@ export interface Datasource { references?: SavedObjectReference[], dataViewsService?: DataViewsPublicPluginStart ) => Promise; + + injectReferencesToLayers?: (state: T, references?: SavedObjectReference[]) => T; } export interface DatasourceFixAction { From 3a495137dd4ca3b6da117f5c4d280209a1647c8b Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 19 Sep 2023 10:27:49 +0300 Subject: [PATCH 24/44] Add more unit tests --- .../public/lib/embeddables/i_embeddable.ts | 1 + .../public/chart/histogram.test.tsx | 10 ++-- .../lens_configuration_flyout.test.tsx | 47 ++++++++++++++++++- .../lens_configuration_flyout.tsx | 2 +- .../open_lens_config/action.test.tsx | 22 +++++++++ 5 files changed, 76 insertions(+), 6 deletions(-) diff --git a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts index 9269e8c45273d..f371208271623 100644 --- a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts +++ b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts @@ -30,6 +30,7 @@ export interface EmbeddableOutput { title?: string; description?: string; editable?: boolean; + // set this to true if the embeddable allows inline editing inlineEditable?: boolean; // Whether the embeddable can be edited inline by re-requesting the explicit input from the user editableWithExplicitInput?: boolean; diff --git a/src/plugins/unified_histogram/public/chart/histogram.test.tsx b/src/plugins/unified_histogram/public/chart/histogram.test.tsx index 375528230323d..528fbda1ab76c 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.test.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.test.tsx @@ -8,6 +8,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { Histogram } from './histogram'; import React from 'react'; +import { of } from 'rxjs'; import { unifiedHistogramServicesMock } from '../__mocks__/services'; import { dataViewWithTimefieldMock } from '../__mocks__/data_view_with_timefield'; import { createDefaultInspectorAdapters } from '@kbn/expressions-plugin/common'; @@ -166,24 +167,25 @@ describe('Histogram', () => { jest .spyOn(adapters.requests, 'getRequests') .mockReturnValue([{ response: { json: { rawResponse } } } as any]); - onLoad(true, undefined); + const embeddableOutput$ = jest.fn().mockReturnValue(of('output$')); + onLoad(true, undefined, embeddableOutput$); expect(props.onTotalHitsChange).toHaveBeenLastCalledWith( UnifiedHistogramFetchStatus.loading, undefined ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters: {} }, undefined); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters: {} }, embeddableOutput$); expect(buildBucketInterval.buildBucketInterval).not.toHaveBeenCalled(); expect(useTimeRange.useTimeRange).toHaveBeenLastCalledWith( expect.objectContaining({ bucketInterval: undefined }) ); act(() => { - onLoad(false, adapters); + onLoad(false, adapters, embeddableOutput$); }); expect(props.onTotalHitsChange).toHaveBeenLastCalledWith( UnifiedHistogramFetchStatus.complete, 100 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, undefined); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, embeddableOutput$); expect(buildBucketInterval.buildBucketInterval).toHaveBeenCalled(); expect(useTimeRange.useTimeRange).toHaveBeenLastCalledWith( expect.objectContaining({ bucketInterval: mockBucketInterval }) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx index 5d4d30aaac640..21626c0fc6f34 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { EuiFlyoutBody } from '@elastic/eui'; +import { EuiFlyoutBody, EuiFlyoutHeader } from '@elastic/eui'; import { mountWithProvider } from '../../../mocks'; import type { Query, AggregateQuery } from '@kbn/es-query'; import { coreMock } from '@kbn/core/public/mocks'; @@ -122,6 +122,20 @@ describe('LensEditConfigurationFlyout', () => { } as unknown as EditConfigPanelProps; } + it('should display the header and the link to editor if necessary props are given', async () => { + const navigateToLensEditorSpy = jest.fn(); + const props = getDefaultProps(); + const newProps = { + ...props, + displayFlyoutHeader: true, + navigateToLensEditor: navigateToLensEditorSpy, + }; + const { instance } = await prepareAndMountComponent(newProps); + expect(instance.find(EuiFlyoutHeader).exists()).toBe(true); + instance.find('[data-test-subj="navigateToLensEditorLink"]').at(1).simulate('click'); + expect(navigateToLensEditorSpy).toHaveBeenCalled(); + }); + it('should call the closeFlyout callback if cancel button is clicked', async () => { const closeFlyoutSpy = jest.fn(); const props = getDefaultProps(); @@ -135,6 +149,37 @@ describe('LensEditConfigurationFlyout', () => { expect(closeFlyoutSpy).toHaveBeenCalled(); }); + it('should call the updateByRefInput callback if cancel button is clicked and savedObjectId exists', async () => { + const updateByRefInputSpy = jest.fn(); + const props = getDefaultProps(); + const newProps = { + ...props, + closeFlyout: jest.fn(), + updateByRefInput: updateByRefInputSpy, + savedObjectId: 'id', + }; + const { instance } = await prepareAndMountComponent(newProps); + instance.find('[data-test-subj="cancelFlyoutButton"]').at(1).simulate('click'); + expect(updateByRefInputSpy).toHaveBeenCalled(); + }); + + it('should call the saveByRef callback if apply button is clicked and savedObjectId exists', async () => { + const updateByRefInputSpy = jest.fn(); + const saveByRefSpy = jest.fn(); + const props = getDefaultProps(); + const newProps = { + ...props, + closeFlyout: jest.fn(), + updateByRefInput: updateByRefInputSpy, + savedObjectId: 'id', + saveByRef: saveByRefSpy, + }; + const { instance } = await prepareAndMountComponent(newProps); + instance.find('[data-test-subj="applyFlyoutButton"]').at(2).simulate('click'); + expect(updateByRefInputSpy).toHaveBeenCalled(); + expect(saveByRefSpy).toHaveBeenCalled(); + }); + it('should compute the frame public api correctly', async () => { const props = getDefaultProps(); const { instance } = await prepareAndMountComponent(props); diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 291c414b6e8f7..de3a0f0669577 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -263,7 +263,7 @@ export function LensEditConfigurationFlyout({ return ( <> {displayFlyoutHeader && ( - + diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx index 530aa12c659d7..d8dbc219c6c17 100644 --- a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx +++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx @@ -43,6 +43,28 @@ describe('open config panel action', () => { expect(isCompatible).toBeFalsy(); }); + it('is incompatible with input view mode', async () => { + const embeddable = { + type: 'NOT_LENS', + getInput: () => { + return { + viewMode: 'view', + }; + }, + } as unknown as IEmbeddable; + const configurablePanelAction = new ConfigureInLensPanelAction( + mockStartDependencies, + overlays, + theme + ); + + const isCompatible = await configurablePanelAction.isCompatible({ + embeddable, + } as ActionExecutionContext<{ embeddable: IEmbeddable }>); + + expect(isCompatible).toBeFalsy(); + }); + it('is compatible with text based language embeddable', async () => { const embeddable = { type: DOC_TYPE, From 52d0ae23690dff47047e407cdfc96fafbf806bce Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 19 Sep 2023 14:32:47 +0300 Subject: [PATCH 25/44] Adds Functional tests --- .../services/dashboard/panel_actions.ts | 12 + .../get_edit_lens_configuration.tsx | 1 - .../lens_configuration_flyout.test.tsx | 4 +- .../lens_configuration_flyout.tsx | 101 +++++---- .../lens/group3/dashboard_inline_editing.ts | 208 ++++++++++++++++++ .../test/functional/apps/lens/group3/index.ts | 1 + 6 files changed, 282 insertions(+), 45 deletions(-) create mode 100644 x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts diff --git a/test/functional/services/dashboard/panel_actions.ts b/test/functional/services/dashboard/panel_actions.ts index ae451525d9a1a..65e5904f14f80 100644 --- a/test/functional/services/dashboard/panel_actions.ts +++ b/test/functional/services/dashboard/panel_actions.ts @@ -99,6 +99,18 @@ export class DashboardPanelActionsService extends FtrService { } } + async clickInlineEdit() { + this.log.debug('clickInlineEditAction'); + await this.expectContextMenuToBeOpen(); + const isInlineEditingActionVisible = await this.testSubjects.exists( + INLINE_EDIT_PANEL_DATA_TEST_SUBJ + ); + if (!isInlineEditingActionVisible) await this.clickContextMenuMoreItem(); + await this.testSubjects.clickWhenNotDisabledWithoutRetry(INLINE_EDIT_PANEL_DATA_TEST_SUBJ); + await this.header.waitUntilLoadingHasFinished(); + await this.common.waitForTopNavToBeVisible(); + } + /** The dashboard/canvas panels can be either edited on their editor or inline. * The inline editing panels allow the navigation to the editor after the flyout opens */ diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index 63f1ec26413b6..3d34072ff3181 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -156,7 +156,6 @@ export async function getEditLensConfiguration( size="s" hideCloseButton css={css` - background: none; clip-path: polygon(-100% 0, 100% 0, 100% 100%, -100% 100%); `} > diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx index 21626c0fc6f34..a2173491a1d28 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { EuiFlyoutBody, EuiFlyoutHeader } from '@elastic/eui'; +import { EuiFlyoutBody } from '@elastic/eui'; import { mountWithProvider } from '../../../mocks'; import type { Query, AggregateQuery } from '@kbn/es-query'; import { coreMock } from '@kbn/core/public/mocks'; @@ -131,7 +131,7 @@ describe('LensEditConfigurationFlyout', () => { navigateToLensEditor: navigateToLensEditorSpy, }; const { instance } = await prepareAndMountComponent(newProps); - expect(instance.find(EuiFlyoutHeader).exists()).toBe(true); + expect(instance.find('[data-test-subj="editFlyoutHeader"]').exists()).toBe(true); instance.find('[data-test-subj="navigateToLensEditorLink"]').at(1).simulate('click'); expect(navigateToLensEditorSpy).toHaveBeenCalled(); }); diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index de3a0f0669577..edacce237346e 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -11,7 +11,6 @@ import { EuiButton, EuiFlyoutBody, EuiFlyoutFooter, - EuiFlyoutHeader, EuiTitle, EuiLink, EuiIcon, @@ -262,43 +261,6 @@ export function LensEditConfigurationFlyout({ }; return ( <> - {displayFlyoutHeader && ( - - - - - - -

- {i18n.translate('xpack.lens.config.editVisualizationLabel', { - defaultMessage: 'Edit visualization', - })} -

-
-
- - - - - -
-
- {navigateToLensEditor && ( - - - {i18n.translate('xpack.lens.config.editLinkLabel', { - defaultMessage: 'Edit in Lens', - })} - - - )} -
-
- )} - - + + {displayFlyoutHeader && ( + + + + + + +

+ {i18n.translate('xpack.lens.config.editVisualizationLabel', { + defaultMessage: 'Edit visualization', + })} +

+
+
+ + + + + +
+
+ {navigateToLensEditor && ( + + + {i18n.translate('xpack.lens.config.editLinkLabel', { + defaultMessage: 'Edit in Lens', + })} + + + )} +
+
+ )} + {datasourceId === 'textBased' && ( - +
diff --git a/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts b/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts new file mode 100644 index 0000000000000..6ca5ededa45d7 --- /dev/null +++ b/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts @@ -0,0 +1,208 @@ +/* + * 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 expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects([ + 'dashboard', + 'visualize', + 'lens', + 'timeToVisualize', + 'common', + 'header', + ]); + const find = getService('find'); + const log = getService('log'); + const listingTable = getService('listingTable'); + const dashboardPanelActions = getService('dashboardPanelActions'); + const testSubjects = getService('testSubjects'); + const elasticChart = getService('elasticChart'); + + const createNewLens = async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await PageObjects.lens.goToTimeRange(); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension', + operation: 'average', + field: 'bytes', + }); + + await PageObjects.lens.switchToVisualization('lnsMetric'); + await PageObjects.lens.waitForVisualization('mtrVis'); + }; + + const loadExistingLens = async () => { + await PageObjects.visualize.gotoVisualizationLandingPage(); + await listingTable.searchForItemWithName('lnsXYvis'); + await PageObjects.lens.clickVisualizeListItemTitle('lnsXYvis'); + await PageObjects.lens.goToTimeRange(); + await PageObjects.lens.waitForVisualization('xyVisChart'); + }; + + describe('lens inline editing tests', () => { + it('should allow inline editing of a by value visualization', async () => { + await createNewLens(); + await PageObjects.lens.save('New Lens from Modal', false, false, false, 'new'); + + await PageObjects.dashboard.waitForRenderComplete(); + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickInlineEdit(); + + log.debug('Adds a secondary dimension'); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsMetric_secondaryMetricDimensionPanel > lns-empty-dimension', + operation: 'max', + field: 'bytes', + keepOpen: true, + }); + + await testSubjects.click('applyFlyoutButton'); + await PageObjects.dashboard.waitForRenderComplete(); + const data = await PageObjects.lens.getMetricVisualizationData(); + const expectedData = [ + { + title: 'Average of bytes', + subtitle: undefined, + extraText: 'Maximum of bytes 19,986', + value: '5,727.322', + color: 'rgba(245, 247, 250, 1)', + showingTrendline: false, + showingBar: false, + }, + ]; + + log.debug(data); + expect(data).to.eql(expectedData); + + await PageObjects.timeToVisualize.resetNewDashboard(); + }); + + it('should allow inline editing of a by reference visualization', async () => { + await loadExistingLens(); + await PageObjects.lens.save('xyVisChart Copy', true, false, false, 'new'); + + await PageObjects.dashboard.waitForRenderComplete(); + await elasticChart.setNewChartUiDebugFlag(true); + + await dashboardPanelActions.saveToLibrary('My by reference visualization'); + + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickInlineEdit(); + + log.debug('Removes breakdown dimension'); + + await PageObjects.lens.removeDimension('lnsXY_splitDimensionPanel'); + + await testSubjects.click('applyFlyoutButton'); + await PageObjects.dashboard.waitForRenderComplete(); + + const data = await PageObjects.lens.getCurrentChartDebugStateForVizType('xyVisChart'); + expect(data?.axes?.y.length).to.eql(1); + await PageObjects.timeToVisualize.resetNewDashboard(); + }); + + it('should reset changes made to the previous state', async () => { + await createNewLens(); + await PageObjects.lens.save('New Lens from Modal', false, false, false, 'new'); + + await PageObjects.dashboard.waitForRenderComplete(); + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickInlineEdit(); + + log.debug('Adds a secondary dimension'); + + await PageObjects.lens.configureDimension({ + dimension: 'lnsMetric_secondaryMetricDimensionPanel > lns-empty-dimension', + operation: 'max', + field: 'bytes', + keepOpen: true, + }); + + log.debug('Cancels the changes'); + + await testSubjects.click('cancelFlyoutButton'); + await PageObjects.dashboard.waitForRenderComplete(); + + const data = await PageObjects.lens.getMetricVisualizationData(); + const expectedData = [ + { + title: 'Average of bytes', + subtitle: undefined, + extraText: '', + value: '5,727.322', + color: 'rgba(245, 247, 250, 1)', + showingTrendline: false, + showingBar: false, + }, + ]; + + expect(data).to.eql(expectedData); + await PageObjects.timeToVisualize.resetNewDashboard(); + }); + + it('should allow adding an annotation', async () => { + await loadExistingLens(); + await PageObjects.lens.save('xyVisChart Copy', true, false, false, 'new'); + + await PageObjects.dashboard.waitForRenderComplete(); + await elasticChart.setNewChartUiDebugFlag(true); + + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickInlineEdit(); + + log.debug('Adds annotation'); + + await PageObjects.lens.createLayer('annotations'); + + expect((await find.allByCssSelector(`[data-test-subj^="lns-layerPanel-"]`)).length).to.eql(2); + expect( + await ( + await testSubjects.find('lnsXY_xAnnotationsPanel > lns-dimensionTrigger') + ).getVisibleText() + ).to.eql('Event'); + + await testSubjects.click('applyFlyoutButton'); + await PageObjects.dashboard.waitForRenderComplete(); + await testSubjects.existOrFail('xyVisAnnotationIcon'); + await PageObjects.timeToVisualize.resetNewDashboard(); + }); + + it('should allow adding a reference line', async () => { + await loadExistingLens(); + await PageObjects.lens.save('xyVisChart Copy', true, false, false, 'new'); + + await PageObjects.dashboard.waitForRenderComplete(); + await elasticChart.setNewChartUiDebugFlag(true); + + await dashboardPanelActions.openContextMenu(); + await dashboardPanelActions.clickInlineEdit(); + + log.debug('Adds reference line'); + + await PageObjects.lens.createLayer('referenceLine'); + + await PageObjects.lens.configureDimension({ + dimension: 'lns-layerPanel-1 > lnsXY_yReferenceLineLeftPanel > lns-dimensionTrigger', + operation: 'formula', + formula: `count()`, + keepOpen: true, + }); + + await PageObjects.lens.selectOptionFromComboBox('lns-icon-select', 'bell'); + + await testSubjects.click('applyFlyoutButton'); + await PageObjects.dashboard.waitForRenderComplete(); + + await testSubjects.existOrFail('xyVisAnnotationIcon'); + + await PageObjects.timeToVisualize.resetNewDashboard(); + }); + }); +} diff --git a/x-pack/test/functional/apps/lens/group3/index.ts b/x-pack/test/functional/apps/lens/group3/index.ts index aa2112f248f71..f82b409b7ba58 100644 --- a/x-pack/test/functional/apps/lens/group3/index.ts +++ b/x-pack/test/functional/apps/lens/group3/index.ts @@ -76,5 +76,6 @@ export default ({ getService, loadTestFile, getPageObjects }: FtrProviderContext loadTestFile(require.resolve('./runtime_fields')); // 1m loadTestFile(require.resolve('./terms')); // 1m 35s loadTestFile(require.resolve('./epoch_millis')); // 30s + loadTestFile(require.resolve('./dashboard_inline_editing')); }); }; From c2324c81516d05ea389e437a090d47624209a599 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Sep 2023 12:11:06 +0300 Subject: [PATCH 26/44] Fix some bugs --- .../lens_configuration_flyout.tsx | 34 +++++++++---------- .../text_based/text_based_languages.tsx | 7 +++- .../config_panel/config_panel.tsx | 1 + .../config_panel/dimension_container.tsx | 1 + .../editor_frame/config_panel/layer_panel.tsx | 2 ++ .../editor_frame/config_panel/types.ts | 1 + .../shared_components/flyout_container.tsx | 34 +++++++++++-------- 7 files changed, 47 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index edacce237346e..bc6a9d53c0666 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -129,24 +129,20 @@ export function LensEditConfigurationFlyout({ const attributesChanged: boolean = useMemo(() => { const previousAttrs = previousAttributes.current; - const prevDatasourceState = datasourceMap[datasourceId].injectReferencesToLayers - ? datasourceMap[datasourceId]?.injectReferencesToLayers?.( - previousAttrs.state.datasourceStates[datasourceId], - previousAttrs.references - ) - : previousAttrs.state.datasourceStates[datasourceId]; - const currentDatasourceState = datasourceMap[datasourceId].injectReferencesToLayers - ? datasourceMap[datasourceId]?.injectReferencesToLayers?.( - datasourceStates[datasourceId].state, - attributes.references - ) - : datasourceStates[datasourceId].state; + const datasourceStatesAreSame = + datasourceStates[datasourceId].state && previousAttrs.state.datasourceStates[datasourceId] + ? datasourceMap[datasourceId].isEqual( + previousAttrs.state.datasourceStates[datasourceId], + previousAttrs.references, + datasourceStates[datasourceId].state, + attributes.references + ) + : false; const visualizationState = visualization.state; return ( - !isEqual(visualizationState, previousAttrs.state.visualization) || - !isEqual(prevDatasourceState, currentDatasourceState) + !isEqual(visualizationState, previousAttrs.state.visualization) || !datasourceStatesAreSame ); }, [attributes.references, datasourceId, datasourceMap, datasourceStates, visualization.state]); @@ -161,9 +157,9 @@ export function LensEditConfigurationFlyout({ ) : previousAttrs.state.datasourceStates[datasourceId]; updatePanelState?.(currentDatasourceState, previousAttrs.state.visualization); - } - if (savedObjectId) { - updateByRefInput?.(savedObjectId); + if (savedObjectId) { + updateByRefInput?.(savedObjectId); + } } closeFlyout?.(); }, [ @@ -258,6 +254,10 @@ export function LensEditConfigurationFlyout({ uiActions: startDependencies.uiActions, hideLayerHeader: datasourceId === 'textBased', indexPatternService, + // we need to hide the footer from the dimensions flyout + // it is not displayed even without hiding this + // but having both in the Dom creates a weird bug with the click events + hideDimensionsFlyoutFooter: true, }; return ( <> diff --git a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx index a76e2edd623b0..43d971caf24a9 100644 --- a/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx +++ b/x-pack/plugins/lens/public/datasources/text_based/text_based_languages.tsx @@ -604,7 +604,12 @@ export function getTextBasedDatasource({ getDatasourceSuggestionsForVisualizeField: getSuggestionsForVisualizeField, getDatasourceSuggestionsFromCurrentState: getSuggestionsForState, getDatasourceSuggestionsForVisualizeCharts: getSuggestionsForState, - isEqual: () => true, + isEqual: ( + persistableState1: TextBasedPersistedState, + references1: SavedObjectReference[], + persistableState2: TextBasedPersistedState, + references2: SavedObjectReference[] + ) => isEqual(persistableState1, persistableState2), getDatasourceInfo: async (state, references, dataViewsService) => { const indexPatterns: DataView[] = []; for (const { index } of Object.values(state.layers)) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 5627916036bf3..7fa69f24d8b41 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -279,6 +279,7 @@ export function LayerPanels( updateDatasource={updateDatasource} updateDatasourceAsync={updateDatasourceAsync} displayLayerSettings={!props.hideLayerHeader} + hideDimensionsFlyoutFooter={props.hideDimensionsFlyoutFooter} onChangeIndexPattern={(args) => { onChangeIndexPattern(args); const layersToRemove = diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index 5a109a6ad36a9..09e08fb25e0c1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -20,6 +20,7 @@ export function DimensionContainer({ groupLabel: string; isFullscreen: boolean; panelRef: (el: HTMLDivElement) => void; + hideFooter?: boolean; }) { return {panel}; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 6472c41a79e02..f3a63c958fca6 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -95,6 +95,7 @@ export function LayerPanel( indexPatternService?: IndexPatternServiceAPI; getUserMessages?: UserMessagesGetter; displayLayerSettings: boolean; + hideDimensionsFlyoutFooter?: boolean; } ) { const [activeDimension, setActiveDimension] = useState( @@ -735,6 +736,7 @@ export function LayerPanel( panelRef={(el) => (panelRef.current = el)} isOpen={isDimensionPanelOpen} isFullscreen={isFullscreen} + hideFooter={props.hideDimensionsFlyoutFooter} groupLabel={activeGroup?.dimensionEditorGroupLabel ?? (activeGroup?.groupLabel || '')} handleClose={() => { if (layerDatasource) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts index 9b1ed7fde9382..d0c31a1cd766e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts @@ -29,6 +29,7 @@ export interface ConfigPanelWrapperProps { uiActions: UiActionsStart; getUserMessages?: UserMessagesGetter; hideLayerHeader?: boolean; + hideDimensionsFlyoutFooter?: boolean; } export interface LayerPanelProps { diff --git a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx index 46a5c5ca39706..dfab3b9045c81 100644 --- a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx @@ -47,6 +47,7 @@ export function FlyoutContainer({ panelContainerRef, children, customFooter, + hideFooter, }: { isOpen: boolean; handleClose: () => boolean; @@ -56,6 +57,7 @@ export function FlyoutContainer({ panelRef?: (el: HTMLDivElement) => void; panelContainerRef?: (el: HTMLDivElement) => void; customFooter?: React.ReactElement; + hideFooter?: boolean; }) { const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false); @@ -142,21 +144,23 @@ export function FlyoutContainer({
{children}
- {customFooter || ( - - - {i18n.translate('xpack.lens.dimensionContainer.close', { - defaultMessage: 'Close', - })} - - - )} + {!Boolean(hideFooter) + ? customFooter || ( + + + {i18n.translate('xpack.lens.dimensionContainer.close', { + defaultMessage: 'Close', + })} + + + ) + : null} From 997e886e71beb8e6c722277e92454cbe5dd22c01 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Sep 2023 13:14:52 +0300 Subject: [PATCH 27/44] Fixes --- .../shared/edit_on_the_fly/lens_configuration_flyout.tsx | 2 +- .../editor_frame/config_panel/layer_panel.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index bc6a9d53c0666..5171f47924c84 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -265,7 +265,7 @@ export function LensEditConfigurationFlyout({ className="lnsEditFlyoutBody" css={css` // styles needed to display extra drop targets that are outside of the config panel main area while also allowing to scroll vertically - overflow-y: scroll; + overflow-y: auto; padding-left: ${euiThemeVars.euiFormMaxWidth}; margin-left: -${euiThemeVars.euiFormMaxWidth}; pointer-events: none !important; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index f3a63c958fca6..946a0b3b3a1bc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -664,6 +664,7 @@ export function LayerPanel( groupLabel={i18n.translate('xpack.lens.editorFrame.layerSettingsTitle', { defaultMessage: 'Layer settings', })} + hideFooter={props.hideDimensionsFlyoutFooter} handleClose={() => { // update the current layer settings setPanelSettingsOpen(false); From 26122aa12d94eb43c7b2a20cc4d1f4236d1d0c48 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Sep 2023 13:31:16 +0300 Subject: [PATCH 28/44] Revert --- .../lens_configuration_flyout.tsx | 6 +--- .../config_panel/dimension_container.tsx | 1 - .../editor_frame/config_panel/layer_panel.tsx | 3 -- .../editor_frame/config_panel/types.ts | 1 - .../shared_components/flyout_container.tsx | 34 ++++++++----------- 5 files changed, 16 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 5171f47924c84..732489deb5e04 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -254,10 +254,6 @@ export function LensEditConfigurationFlyout({ uiActions: startDependencies.uiActions, hideLayerHeader: datasourceId === 'textBased', indexPatternService, - // we need to hide the footer from the dimensions flyout - // it is not displayed even without hiding this - // but having both in the Dom creates a weird bug with the click events - hideDimensionsFlyoutFooter: true, }; return ( <> @@ -265,7 +261,7 @@ export function LensEditConfigurationFlyout({ className="lnsEditFlyoutBody" css={css` // styles needed to display extra drop targets that are outside of the config panel main area while also allowing to scroll vertically - overflow-y: auto; + overflow-y: scroll; padding-left: ${euiThemeVars.euiFormMaxWidth}; margin-left: -${euiThemeVars.euiFormMaxWidth}; pointer-events: none !important; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index 09e08fb25e0c1..5a109a6ad36a9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -20,7 +20,6 @@ export function DimensionContainer({ groupLabel: string; isFullscreen: boolean; panelRef: (el: HTMLDivElement) => void; - hideFooter?: boolean; }) { return {panel}; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 946a0b3b3a1bc..6472c41a79e02 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -95,7 +95,6 @@ export function LayerPanel( indexPatternService?: IndexPatternServiceAPI; getUserMessages?: UserMessagesGetter; displayLayerSettings: boolean; - hideDimensionsFlyoutFooter?: boolean; } ) { const [activeDimension, setActiveDimension] = useState( @@ -664,7 +663,6 @@ export function LayerPanel( groupLabel={i18n.translate('xpack.lens.editorFrame.layerSettingsTitle', { defaultMessage: 'Layer settings', })} - hideFooter={props.hideDimensionsFlyoutFooter} handleClose={() => { // update the current layer settings setPanelSettingsOpen(false); @@ -737,7 +735,6 @@ export function LayerPanel( panelRef={(el) => (panelRef.current = el)} isOpen={isDimensionPanelOpen} isFullscreen={isFullscreen} - hideFooter={props.hideDimensionsFlyoutFooter} groupLabel={activeGroup?.dimensionEditorGroupLabel ?? (activeGroup?.groupLabel || '')} handleClose={() => { if (layerDatasource) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts index d0c31a1cd766e..9b1ed7fde9382 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts @@ -29,7 +29,6 @@ export interface ConfigPanelWrapperProps { uiActions: UiActionsStart; getUserMessages?: UserMessagesGetter; hideLayerHeader?: boolean; - hideDimensionsFlyoutFooter?: boolean; } export interface LayerPanelProps { diff --git a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx index dfab3b9045c81..46a5c5ca39706 100644 --- a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx @@ -47,7 +47,6 @@ export function FlyoutContainer({ panelContainerRef, children, customFooter, - hideFooter, }: { isOpen: boolean; handleClose: () => boolean; @@ -57,7 +56,6 @@ export function FlyoutContainer({ panelRef?: (el: HTMLDivElement) => void; panelContainerRef?: (el: HTMLDivElement) => void; customFooter?: React.ReactElement; - hideFooter?: boolean; }) { const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false); @@ -144,23 +142,21 @@ export function FlyoutContainer({
{children}
- {!Boolean(hideFooter) - ? customFooter || ( - - - {i18n.translate('xpack.lens.dimensionContainer.close', { - defaultMessage: 'Close', - })} - - - ) - : null} + {customFooter || ( + + + {i18n.translate('xpack.lens.dimensionContainer.close', { + defaultMessage: 'Close', + })} + + + )} From 34ea0b78cc7e26d853703c192220fb8a5f2a1cfd Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Sep 2023 13:49:29 +0300 Subject: [PATCH 29/44] Fixes CI --- .../editor_frame/config_panel/config_panel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 7fa69f24d8b41..5627916036bf3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -279,7 +279,6 @@ export function LayerPanels( updateDatasource={updateDatasource} updateDatasourceAsync={updateDatasourceAsync} displayLayerSettings={!props.hideLayerHeader} - hideDimensionsFlyoutFooter={props.hideDimensionsFlyoutFooter} onChangeIndexPattern={(args) => { onChangeIndexPattern(args); const layersToRemove = From b2dfd6c6ec8b095c8fe479cfc86ddc7bf7653789 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Sep 2023 14:26:30 +0300 Subject: [PATCH 30/44] Adds back navigation buttons --- .../lens_configuration_flyout.tsx | 75 ++++++++++--------- .../config_panel/dimension_container.tsx | 1 + .../editor_frame/config_panel/layer_panel.tsx | 8 ++ .../editor_frame/config_panel/types.ts | 1 + .../shared_components/flyout_container.tsx | 54 +++++++++---- 5 files changed, 89 insertions(+), 50 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 732489deb5e04..e1a162ce72519 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useMemo, useCallback, useRef, useEffect } from 'react'; +import React, { useMemo, useCallback, useRef, useEffect, useState } from 'react'; import { EuiButtonEmpty, EuiButton, @@ -107,6 +107,7 @@ export function LensEditConfigurationFlyout({ const datasourceState = attributes.state.datasourceStates[datasourceId]; const activeVisualization = visualizationMap[attributes.visualizationType]; const activeDatasource = datasourceMap[datasourceId]; + const [isInlineFooterVisible, setIsInlineFlyoutFooterVisible] = useState(true); const { euiTheme } = useEuiTheme(); const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); const dispatch = useLensDispatch(); @@ -254,6 +255,7 @@ export function LensEditConfigurationFlyout({ uiActions: startDependencies.uiActions, hideLayerHeader: datasourceId === 'textBased', indexPatternService, + setIsInlineFlyoutFooterVisible, }; return ( <> @@ -353,39 +355,44 @@ export function LensEditConfigurationFlyout({
- - - - - - - - - - - - - - + {isInlineFooterVisible && ( + + + + + + + + + + + + + + + )} ); } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index 5a109a6ad36a9..6afc4069ec7ee 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -20,6 +20,7 @@ export function DimensionContainer({ groupLabel: string; isFullscreen: boolean; panelRef: (el: HTMLDivElement) => void; + isInlineEditing?: boolean; }) { return {panel}; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 6472c41a79e02..dd2810a2aeec8 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -95,6 +95,7 @@ export function LayerPanel( indexPatternService?: IndexPatternServiceAPI; getUserMessages?: UserMessagesGetter; displayLayerSettings: boolean; + setIsInlineFlyoutFooterVisible?: (status: boolean) => void; } ) { const [activeDimension, setActiveDimension] = useState( @@ -135,6 +136,12 @@ export function LayerPanel( setActiveDimension(initialActiveDimensionState); }, [activeVisualization.id]); + useEffect(() => { + // is undefined when the dimension panel is closed + const activeDimensionId = activeDimension.activeId; + props?.setIsInlineFlyoutFooterVisible?.(!Boolean(activeDimensionId)); + }, [activeDimension.activeId, activeVisualization.id, props]); + const panelRef = useRef(null); const settingsPanelRef = useRef(null); @@ -736,6 +743,7 @@ export function LayerPanel( isOpen={isDimensionPanelOpen} isFullscreen={isFullscreen} groupLabel={activeGroup?.dimensionEditorGroupLabel ?? (activeGroup?.groupLabel || '')} + isInlineEditing={Boolean(props?.setIsInlineFlyoutFooterVisible)} handleClose={() => { if (layerDatasource) { if (layerDatasource.updateStateOnCloseDimension) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts index 9b1ed7fde9382..6d06dfb7e6aac 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/types.ts @@ -29,6 +29,7 @@ export interface ConfigPanelWrapperProps { uiActions: UiActionsStart; getUserMessages?: UserMessagesGetter; hideLayerHeader?: boolean; + setIsInlineFlyoutFooterVisible?: (status: boolean) => void; } export interface LayerPanelProps { diff --git a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx index 46a5c5ca39706..7ecbccd4229c3 100644 --- a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx @@ -47,6 +47,7 @@ export function FlyoutContainer({ panelContainerRef, children, customFooter, + isInlineEditing, }: { isOpen: boolean; handleClose: () => boolean; @@ -56,6 +57,7 @@ export function FlyoutContainer({ panelRef?: (el: HTMLDivElement) => void; panelContainerRef?: (el: HTMLDivElement) => void; customFooter?: React.ReactElement; + isInlineEditing?: boolean; }) { const [focusTrapIsEnabled, setFocusTrapIsEnabled] = useState(false); @@ -109,6 +111,20 @@ export function FlyoutContainer({ > + {isInlineEditing && ( + + + + )}

- - - + {!isInlineEditing && ( + + + + )} @@ -147,13 +165,17 @@ export function FlyoutContainer({ - {i18n.translate('xpack.lens.dimensionContainer.close', { - defaultMessage: 'Close', - })} + {isInlineEditing + ? i18n.translate('xpack.lens.dimensionContainer.back', { + defaultMessage: 'Back', + }) + : i18n.translate('xpack.lens.dimensionContainer.close', { + defaultMessage: 'Close', + })} )} From 93ca7d9706f7e3b53e5b7d24a5ad2abe10c73e0a Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Sep 2023 16:35:46 +0300 Subject: [PATCH 31/44] Fixes inline tests --- .../edit_on_the_fly/get_edit_lens_configuration.tsx | 1 + .../state_management/init_middleware/load_initial.ts | 12 +++++++++--- .../lens/public/state_management/lens_slice.ts | 2 ++ .../apps/lens/group3/dashboard_inline_editing.ts | 4 ++-- x-pack/test/functional/page_objects/lens_page.ts | 1 + 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx index fdfb67b3779f0..fc6511b66ec15 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration.tsx @@ -138,6 +138,7 @@ export async function getEditLensConfiguration( attributes, id: panelId ?? generateId(), }, + inlineEditing: true, }) ); diff --git a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts index c33ed7dd07ba2..418fcc2308e7e 100644 --- a/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts +++ b/x-pack/plugins/lens/public/state_management/init_middleware/load_initial.ts @@ -91,10 +91,12 @@ export function loadInitial( redirectCallback, initialInput, history, + inlineEditing, }: { redirectCallback?: (savedObjectId?: string) => void; initialInput?: LensEmbeddableInput; history?: History; + inlineEditing?: boolean; }, autoApplyDisabled: boolean ) { @@ -291,9 +293,13 @@ export function loadInitial( {} ); - const filters = data.query.filterManager.inject(doc.state.filters, doc.references); - // Don't overwrite any pinned filters - data.query.filterManager.setAppFilters(filters); + // when the embeddable is initialized from the dashboard we don't want to inject the filters + // as this will replace the parent application filters (such as a dashboard) + if (!Boolean(inlineEditing)) { + const filters = data.query.filterManager.inject(doc.state.filters, doc.references); + // Don't overwrite any pinned filters + data.query.filterManager.setAppFilters(filters); + } const docVisualizationState = { activeId: doc.visualizationType, diff --git a/x-pack/plugins/lens/public/state_management/lens_slice.ts b/x-pack/plugins/lens/public/state_management/lens_slice.ts index 8d24cf00d1d70..df3564958111b 100644 --- a/x-pack/plugins/lens/public/state_management/lens_slice.ts +++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts @@ -203,6 +203,7 @@ export const loadInitial = createAction<{ initialInput?: LensEmbeddableInput; redirectCallback?: (savedObjectId?: string) => void; history?: History; + inlineEditing?: boolean; }>('lens/loadInitial'); export const initEmpty = createAction( 'initEmpty', @@ -846,6 +847,7 @@ export const makeLensReducer = (storeDeps: LensStoreDeps) => { initialInput?: LensEmbeddableInput; redirectCallback?: (savedObjectId?: string) => void; history?: History; + inlineEditing?: boolean; }> ) => state, [initEmpty.type]: ( diff --git a/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts b/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts index 6ca5ededa45d7..c31489f08ba35 100644 --- a/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts +++ b/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts @@ -62,7 +62,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { field: 'bytes', keepOpen: true, }); - + await PageObjects.lens.closeDimensionEditor(); await testSubjects.click('applyFlyoutButton'); await PageObjects.dashboard.waitForRenderComplete(); const data = await PageObjects.lens.getMetricVisualizationData(); @@ -196,7 +196,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); await PageObjects.lens.selectOptionFromComboBox('lns-icon-select', 'bell'); - + await PageObjects.lens.closeDimensionEditor(); await testSubjects.click('applyFlyoutButton'); await PageObjects.dashboard.waitForRenderComplete(); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index 3c0097bdedb8c..493977e7fc735 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -1472,6 +1472,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont } if (!opts.keepOpen) { + await this.closeDimensionEditor(); await testSubjects.click('applyFlyoutButton'); } }, From 1ccef1a25066ffe90f3e47a50e4a97557f0227e7 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 20 Sep 2023 17:41:55 +0300 Subject: [PATCH 32/44] Fixes last FT fai; --- .../functional/apps/lens/group3/dashboard_inline_editing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts b/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts index c31489f08ba35..da5858e0ffc1d 100644 --- a/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts +++ b/x-pack/test/functional/apps/lens/group3/dashboard_inline_editing.ts @@ -126,7 +126,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); log.debug('Cancels the changes'); - + await PageObjects.lens.closeDimensionEditor(); await testSubjects.click('cancelFlyoutButton'); await PageObjects.dashboard.waitForRenderComplete(); From cebc687c9a6aaae55a4c0db61c1eccb2f9453fe8 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 22 Sep 2023 16:09:48 +0300 Subject: [PATCH 33/44] Update src/plugins/unified_histogram/public/container/hooks/use_state_props.ts Co-authored-by: Marco Liberati --- .../unified_histogram/public/container/hooks/use_state_props.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts index ecc0d7d863d20..8ce580938dd9a 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts @@ -14,7 +14,7 @@ import { Query, } from '@kbn/es-query'; import type { RequestAdapter } from '@kbn/inspector-plugin/public'; -import { LensEmbeddableOutput } from '@kbn/lens-plugin/public'; +import type { LensEmbeddableOutput } from '@kbn/lens-plugin/public'; import { useCallback, useEffect, useMemo } from 'react'; import { Observable } from 'rxjs'; import { UnifiedHistogramChartLoadEvent, UnifiedHistogramFetchStatus } from '../../types'; From 96294f4e4111909ba9054a770f217b33f0e633da Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 22 Sep 2023 16:10:02 +0300 Subject: [PATCH 34/44] Update src/plugins/unified_histogram/public/container/hooks/use_state_props.ts Co-authored-by: Marco Liberati --- .../unified_histogram/public/container/hooks/use_state_props.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts index 8ce580938dd9a..d68a2f6d47db8 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts @@ -16,7 +16,7 @@ import { import type { RequestAdapter } from '@kbn/inspector-plugin/public'; import type { LensEmbeddableOutput } from '@kbn/lens-plugin/public'; import { useCallback, useEffect, useMemo } from 'react'; -import { Observable } from 'rxjs'; +import type { Observable } from 'rxjs'; import { UnifiedHistogramChartLoadEvent, UnifiedHistogramFetchStatus } from '../../types'; import type { UnifiedHistogramStateService } from '../services/state_service'; import { From 96f8d52229a897df1636270e82629ec5f55805b6 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 22 Sep 2023 16:10:12 +0300 Subject: [PATCH 35/44] Update src/plugins/unified_histogram/public/chart/chart_config_panel.tsx Co-authored-by: Marco Liberati --- .../unified_histogram/public/chart/chart_config_panel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx index 4a8973e746e28..486ea7da79872 100644 --- a/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx +++ b/src/plugins/unified_histogram/public/chart/chart_config_panel.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { Observable } from 'rxjs'; +import type { Observable } from 'rxjs'; import type { AggregateQuery, Query } from '@kbn/es-query'; import { isEqual } from 'lodash'; import type { LensEmbeddableOutput, Suggestion } from '@kbn/lens-plugin/public'; From 49acc97b94736cf2416fee50c84d04c3fce1a008 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 22 Sep 2023 17:27:48 +0300 Subject: [PATCH 36/44] Fixes Safari scroll issue --- .../shared/edit_on_the_fly/lens_configuration_flyout.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index bbc3bf28f09bc..94839e0174e94 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -266,7 +266,6 @@ export function LensEditConfigurationFlyout({ overflow-y: auto; padding-left: ${euiThemeVars.euiFormMaxWidth}; margin-left: -${euiThemeVars.euiFormMaxWidth}; - pointer-events: none !important; .euiFlyoutBody__overflow { padding-left: inherit; margin-left: inherit; From 1056df83c47428928ea8b8159d96cbe91f61dbee Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 22 Sep 2023 17:39:25 +0300 Subject: [PATCH 37/44] Removes the extra shadown from the dimension flyout when in editing mode --- .../lens/public/shared_components/flyout_container.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx index 7ecbccd4229c3..6850d4cf5d49a 100644 --- a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx @@ -8,6 +8,7 @@ import './flyout_container.scss'; import React, { useState, useEffect, useCallback } from 'react'; +import { css } from '@emotion/react'; import { EuiFlyoutHeader, EuiFlyoutFooter, @@ -101,6 +102,9 @@ export function FlyoutContainer({ role="dialog" aria-labelledby="lnsDimensionContainerTitle" className="lnsDimensionContainer" + css={css` + box-shadow: ${isInlineEditing ? 'none !important' : 'inherit'}; + `} onAnimationEnd={() => { if (isOpen) { // EuiFocusTrap interferes with animating elements with absolute position: From f9ecad4743bc7757bd6eeb820278ac459b081c6c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 25 Sep 2023 12:28:43 +0300 Subject: [PATCH 38/44] Apply PR comments --- .../shared_components/flyout_container.tsx | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx index 6850d4cf5d49a..d06c9babed5c5 100644 --- a/x-pack/plugins/lens/public/shared_components/flyout_container.tsx +++ b/x-pack/plugins/lens/public/shared_components/flyout_container.tsx @@ -71,14 +71,16 @@ export function FlyoutContainer({ }, [handleClose]); useEffect(() => { - document.body.classList.toggle('lnsBody--overflowHidden', isOpen); - return () => { - if (isOpen) { - setFocusTrapIsEnabled(false); - } - document.body.classList.remove('lnsBody--overflowHidden'); - }; - }, [isOpen]); + if (!isInlineEditing) { + document.body.classList.toggle('lnsBody--overflowHidden', isOpen); + return () => { + if (isOpen) { + setFocusTrapIsEnabled(false); + } + document.body.classList.remove('lnsBody--overflowHidden'); + }; + } + }, [isInlineEditing, isOpen]); if (!isOpen) { return null; @@ -108,8 +110,9 @@ export function FlyoutContainer({ onAnimationEnd={() => { if (isOpen) { // EuiFocusTrap interferes with animating elements with absolute position: - // running this onAnimationEnd, otherwise the flyout pushes content when animating - setFocusTrapIsEnabled(true); + // running this onAnimationEnd, otherwise the flyout pushes content when animating. + // The EuiFocusTrap is disabled when inline editing as it causes bugs with comboboxes + setFocusTrapIsEnabled(!Boolean(isInlineEditing)); } }} > From 17b910f28d40d4d1b639aff4f782f8ebbda48796 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 25 Sep 2023 13:50:15 +0300 Subject: [PATCH 39/44] Fixes dancing in layer settings too --- .../editor_frame/config_panel/layer_panel.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index dd2810a2aeec8..bd5cf0b78b20f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -675,6 +675,7 @@ export function LayerPanel( setPanelSettingsOpen(false); return true; }} + isInlineEditing={Boolean(props?.setIsInlineFlyoutFooterVisible)} >
From 6f32df680177e70b0b8a9aba8acf04917af7906c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Mon, 25 Sep 2023 14:03:39 +0300 Subject: [PATCH 40/44] Removes dancing from the load annotations from library flyout --- .../editor_frame/config_panel/config_panel.tsx | 1 + x-pack/plugins/lens/public/types.ts | 1 + x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx | 3 +++ .../visualizations/xy/load_annotation_library_flyout.tsx | 3 +++ 4 files changed, 8 insertions(+) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index 5627916036bf3..41184d2212c45 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -370,6 +370,7 @@ export function LayerPanels( } }, registerLibraryAnnotationGroup: registerLibraryAnnotationGroupFunction, + isInlineEditing: Boolean(props?.setIsInlineFlyoutFooterVisible), })} ); diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 3f98c0e906dbe..d549fbb71bdcf 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -1011,6 +1011,7 @@ interface AddLayerButtonProps { addLayer: AddLayerFunction; ensureIndexPattern: (specOrId: DataViewSpec | string) => Promise; registerLibraryAnnotationGroup: RegisterLibraryAnnotationGroupFunction; + isInlineEditing?: boolean; } export interface Visualization { diff --git a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx index c522badb5b96d..3e4ecfd0d5bfe 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/add_layer.tsx @@ -26,12 +26,14 @@ interface AddLayerButtonProps { supportedLayers: VisualizationLayerDescription[]; addLayer: AddLayerFunction; eventAnnotationService: EventAnnotationServiceType; + isInlineEditing?: boolean; } export function AddLayerButton({ supportedLayers, addLayer, eventAnnotationService, + isInlineEditing, }: AddLayerButtonProps) { const [showLayersChoice, toggleLayersChoice] = useState(false); @@ -162,6 +164,7 @@ export function AddLayerButton({ isLoadLibraryVisible={isLoadLibraryVisible} setLoadLibraryFlyoutVisible={setLoadLibraryFlyoutVisible} eventAnnotationService={eventAnnotationService} + isInlineEditing={isInlineEditing} addLayer={(extraArg) => { addLayer(LayerTypes.ANNOTATIONS, extraArg); }} diff --git a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx index cb9521b42e0a6..098d76e7f9ad4 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx +++ b/x-pack/plugins/lens/public/visualizations/xy/load_annotation_library_flyout.tsx @@ -19,11 +19,13 @@ export function LoadAnnotationLibraryFlyout({ isLoadLibraryVisible, setLoadLibraryFlyoutVisible, addLayer, + isInlineEditing, }: { isLoadLibraryVisible: boolean; setLoadLibraryFlyoutVisible: (visible: boolean) => void; eventAnnotationService: EventAnnotationServiceType; addLayer: (argument?: ExtraAppendLayerArg) => void; + isInlineEditing?: boolean; }) { const { renderEventAnnotationGroupSavedObjectFinder: EventAnnotationGroupSavedObjectFinder, @@ -66,6 +68,7 @@ export function LoadAnnotationLibraryFlyout({ setLoadLibraryFlyoutVisible(false); return true; }} + isInlineEditing={isInlineEditing} >
Date: Tue, 26 Sep 2023 11:00:57 +0300 Subject: [PATCH 41/44] Fixes broken types --- .../public/applications/analytics/hoc/with_lens_data.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.tsx index 64df74a011c88..010ef0e7f496c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/hoc/with_lens_data.tsx @@ -96,9 +96,9 @@ export const withLensData = ( attributes={attributes} searchSessionId={props?.searchSessionId} onBrushEnd={handleBrushEnd} - onLoad={(...args) => { + onLoad={(isLoading, adapters) => { if (dataLoadTransform) { - setData(dataLoadTransform(...args)); + setData(dataLoadTransform(isLoading, adapters)); } }} /> From ca55c78031b09cabd06ac675795395bb22dda8a8 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 26 Sep 2023 11:16:53 +0300 Subject: [PATCH 42/44] Fixes pointer events problem --- .../shared/edit_on_the_fly/lens_configuration_flyout.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx index 94839e0174e94..a60e3df063aaa 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/lens_configuration_flyout.tsx @@ -266,6 +266,7 @@ export function LensEditConfigurationFlyout({ overflow-y: auto; padding-left: ${euiThemeVars.euiFormMaxWidth}; margin-left: -${euiThemeVars.euiFormMaxWidth}; + pointer-events: none; .euiFlyoutBody__overflow { padding-left: inherit; margin-left: inherit; From 83ed9a475a242e5b8959162949ab8e4f53903f7c Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Tue, 26 Sep 2023 12:25:03 +0300 Subject: [PATCH 43/44] Address PR comments --- src/plugins/unified_histogram/public/chart/chart.tsx | 5 +---- .../unified_histogram/public/chart/histogram.test.tsx | 10 +++++----- .../unified_histogram/public/chart/histogram.tsx | 7 ++----- .../public/container/hooks/use_state_props.ts | 9 ++------- src/plugins/unified_histogram/public/types.ts | 8 ++++++-- 5 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/plugins/unified_histogram/public/chart/chart.tsx b/src/plugins/unified_histogram/public/chart/chart.tsx index 401405273692a..42f3d56584789 100644 --- a/src/plugins/unified_histogram/public/chart/chart.tsx +++ b/src/plugins/unified_histogram/public/chart/chart.tsx @@ -83,10 +83,7 @@ export interface ChartProps { onBreakdownFieldChange?: (breakdownField: DataViewField | undefined) => void; onSuggestionChange?: (suggestion: Suggestion | undefined) => void; onTotalHitsChange?: (status: UnifiedHistogramFetchStatus, result?: number | Error) => void; - onChartLoad?: ( - event: UnifiedHistogramChartLoadEvent, - lensEmbeddableOutput$?: Observable - ) => void; + onChartLoad?: (event: UnifiedHistogramChartLoadEvent) => void; onFilter?: LensEmbeddableInput['onFilter']; onBrushEnd?: LensEmbeddableInput['onBrushEnd']; withDefaultActions: EmbeddableComponentProps['withDefaultActions']; diff --git a/src/plugins/unified_histogram/public/chart/histogram.test.tsx b/src/plugins/unified_histogram/public/chart/histogram.test.tsx index 528fbda1ab76c..8fd749051f2ce 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.test.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.test.tsx @@ -173,7 +173,7 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.loading, undefined ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters: {} }, embeddableOutput$); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters: {}, embeddableOutput$ }); expect(buildBucketInterval.buildBucketInterval).not.toHaveBeenCalled(); expect(useTimeRange.useTimeRange).toHaveBeenLastCalledWith( expect.objectContaining({ bucketInterval: undefined }) @@ -185,7 +185,7 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.complete, 100 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, embeddableOutput$); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters, embeddableOutput$ }); expect(buildBucketInterval.buildBucketInterval).toHaveBeenCalled(); expect(useTimeRange.useTimeRange).toHaveBeenLastCalledWith( expect.objectContaining({ bucketInterval: mockBucketInterval }) @@ -238,7 +238,7 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.complete, 100 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, undefined); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }); }); it('should execute onLoad correctly for textbased language and no Lens suggestions', async () => { @@ -274,7 +274,7 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.complete, 20 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, undefined); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }); }); it('should execute onLoad correctly for textbased language and Lens suggestions', async () => { @@ -310,6 +310,6 @@ describe('Histogram', () => { UnifiedHistogramFetchStatus.complete, 2 ); - expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }, undefined); + expect(props.onChartLoad).toHaveBeenLastCalledWith({ adapters }); }); }); diff --git a/src/plugins/unified_histogram/public/chart/histogram.tsx b/src/plugins/unified_histogram/public/chart/histogram.tsx index 6d3af6aea3e8e..1d91b2a505174 100644 --- a/src/plugins/unified_histogram/public/chart/histogram.tsx +++ b/src/plugins/unified_histogram/public/chart/histogram.tsx @@ -51,10 +51,7 @@ export interface HistogramProps { disableTriggers?: LensEmbeddableInput['disableTriggers']; disabledActions?: LensEmbeddableInput['disabledActions']; onTotalHitsChange?: (status: UnifiedHistogramFetchStatus, result?: number | Error) => void; - onChartLoad?: ( - event: UnifiedHistogramChartLoadEvent, - lensEmbeddableOutput$?: Observable - ) => void; + onChartLoad?: (event: UnifiedHistogramChartLoadEvent) => void; onFilter?: LensEmbeddableInput['onFilter']; onBrushEnd?: LensEmbeddableInput['onBrushEnd']; withDefaultActions: EmbeddableComponentProps['withDefaultActions']; @@ -166,7 +163,7 @@ export function Histogram({ setBucketInterval(newBucketInterval); } - onChartLoad?.({ adapters: adapters ?? {} }, lensEmbeddableOutput$); + onChartLoad?.({ adapters: adapters ?? {}, embeddableOutput$: lensEmbeddableOutput$ }); } ); diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts index d68a2f6d47db8..8aa0341fe256e 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts @@ -14,9 +14,7 @@ import { Query, } from '@kbn/es-query'; import type { RequestAdapter } from '@kbn/inspector-plugin/public'; -import type { LensEmbeddableOutput } from '@kbn/lens-plugin/public'; import { useCallback, useEffect, useMemo } from 'react'; -import type { Observable } from 'rxjs'; import { UnifiedHistogramChartLoadEvent, UnifiedHistogramFetchStatus } from '../../types'; import type { UnifiedHistogramStateService } from '../services/state_service'; import { @@ -144,14 +142,11 @@ export const useStateProps = ({ ); const onChartLoad = useCallback( - ( - event: UnifiedHistogramChartLoadEvent, - embeddablelensEmbeddableOutput$?: Observable - ) => { + (event: UnifiedHistogramChartLoadEvent) => { // We need to store the Lens request adapter in order to inspect its requests stateService?.setLensRequestAdapter(event.adapters.requests); stateService?.setLensAdapters(event.adapters); - stateService?.setlensEmbeddableOutput$(embeddablelensEmbeddableOutput$); + stateService?.setlensEmbeddableOutput$(event.embeddableOutput$); }, [stateService] ); diff --git a/src/plugins/unified_histogram/public/types.ts b/src/plugins/unified_histogram/public/types.ts index 803c74304a3a6..3ba27f7c5b26e 100644 --- a/src/plugins/unified_histogram/public/types.ts +++ b/src/plugins/unified_histogram/public/types.ts @@ -9,11 +9,11 @@ import type { IUiSettingsClient, Capabilities } from '@kbn/core/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import type { LensPublicStart } from '@kbn/lens-plugin/public'; +import type { LensEmbeddableOutput, LensPublicStart } from '@kbn/lens-plugin/public'; import type { DataViewField } from '@kbn/data-views-plugin/public'; import type { RequestAdapter } from '@kbn/inspector-plugin/public'; import type { DefaultInspectorAdapters } from '@kbn/expressions-plugin/common'; -import type { Subject } from 'rxjs'; +import type { Observable, Subject } from 'rxjs'; import type { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; @@ -65,6 +65,10 @@ export interface UnifiedHistogramChartLoadEvent { * Inspector adapters for the request */ adapters: UnifiedHistogramAdapters; + /** + * Observable of the lens embeddable output + */ + embeddableOutput$?: Observable; } /** From 5d41f4f2e3538fc7030ddb18814a15e010bd5085 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Wed, 27 Sep 2023 09:20:28 +0300 Subject: [PATCH 44/44] Fix --- .../public/container/hooks/use_state_props.ts | 2 +- .../public/container/services/state_service.test.ts | 2 +- .../public/container/services/state_service.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts index 8aa0341fe256e..d78afc50c15f5 100644 --- a/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts +++ b/src/plugins/unified_histogram/public/container/hooks/use_state_props.ts @@ -146,7 +146,7 @@ export const useStateProps = ({ // We need to store the Lens request adapter in order to inspect its requests stateService?.setLensRequestAdapter(event.adapters.requests); stateService?.setLensAdapters(event.adapters); - stateService?.setlensEmbeddableOutput$(event.embeddableOutput$); + stateService?.setLensEmbeddableOutput$(event.embeddableOutput$); }, [stateService] ); diff --git a/src/plugins/unified_histogram/public/container/services/state_service.test.ts b/src/plugins/unified_histogram/public/container/services/state_service.test.ts index ee0905da937fd..73a493e167c19 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.test.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.test.ts @@ -139,7 +139,7 @@ describe('UnifiedHistogramStateService', () => { stateService.setLensAdapters(undefined); newState = { ...newState, lensAdapters: undefined }; expect(state).toEqual(newState); - stateService.setlensEmbeddableOutput$(undefined); + stateService.setLensEmbeddableOutput$(undefined); newState = { ...newState, lensEmbeddableOutput$: undefined }; expect(state).toEqual(newState); stateService.setTotalHits({ diff --git a/src/plugins/unified_histogram/public/container/services/state_service.ts b/src/plugins/unified_histogram/public/container/services/state_service.ts index 1768c6cca947f..f96a4b5b7b033 100644 --- a/src/plugins/unified_histogram/public/container/services/state_service.ts +++ b/src/plugins/unified_histogram/public/container/services/state_service.ts @@ -120,7 +120,7 @@ export interface UnifiedHistogramStateService { * Sets the current Lens adapters */ setLensAdapters: (lensAdapters: UnifiedHistogramChartLoadEvent['adapters'] | undefined) => void; - setlensEmbeddableOutput$: ( + setLensEmbeddableOutput$: ( lensEmbeddableOutput$: Observable | undefined ) => void; /** @@ -207,7 +207,7 @@ export const createStateService = ( setLensAdapters: (lensAdapters: UnifiedHistogramChartLoadEvent['adapters'] | undefined) => { updateState({ lensAdapters }); }, - setlensEmbeddableOutput$: ( + setLensEmbeddableOutput$: ( lensEmbeddableOutput$: Observable | undefined ) => { updateState({ lensEmbeddableOutput$ });