diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts
index 3b28dafbcd761..def26211291d6 100644
--- a/packages/kbn-doc-links/src/get_doc_links.ts
+++ b/packages/kbn-doc-links/src/get_doc_links.ts
@@ -139,6 +139,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => {
connectorsDropbox: `${ENTERPRISE_SEARCH_DOCS}connectors-dropbox.html`,
connectorsContentExtraction: `${ENTERPRISE_SEARCH_DOCS}connectors-content-extraction.html`,
connectorsGoogleCloudStorage: `${ENTERPRISE_SEARCH_DOCS}connectors-google-cloud.html`,
+ connectorsGoogleDrive: `${ENTERPRISE_SEARCH_DOCS}connectors-google-drive.html`,
connectorsJira: `${ENTERPRISE_SEARCH_DOCS}connectors-jira.html`,
connectorsMicrosoftSQL: `${ENTERPRISE_SEARCH_DOCS}connectors-ms-sql.html`,
connectorsMongoDB: `${ENTERPRISE_SEARCH_DOCS}connectors-mongodb.html`,
diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts
index b8f15cbde68ca..c4fdc8657e2ac 100644
--- a/packages/kbn-doc-links/src/types.ts
+++ b/packages/kbn-doc-links/src/types.ts
@@ -123,6 +123,7 @@ export interface DocLinks {
readonly connectorsContentExtraction: string;
readonly connectorsDropbox: string;
readonly connectorsGoogleCloudStorage: string;
+ readonly connectorsGoogleDrive: string;
readonly connectorsJira: string;
readonly connectorsMicrosoftSQL: string;
readonly connectorsMongoDB: string;
diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml
index cd654512f332b..3a1a8387feaa7 100644
--- a/packages/kbn-optimizer/limits.yml
+++ b/packages/kbn-optimizer/limits.yml
@@ -83,7 +83,7 @@ pageLoadAssetSize:
kibanaUsageCollection: 16463
kibanaUtils: 79713
kubernetesSecurity: 77234
- lens: 37000
+ lens: 38000
licenseManagement: 41817
licensing: 29004
lists: 22900
diff --git a/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.tsx b/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.tsx
index 636c88d563470..eab6c89c38844 100644
--- a/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.tsx
+++ b/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover.tsx
@@ -40,6 +40,7 @@ export function FiltersNotificationPopover({
}: FiltersNotificationProps) {
const { embeddable } = context;
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
+ const [disableEditbutton, setDisableEditButton] = useState(false);
return (
{displayName}
-
+
-
-
- editPanelAction.execute({ embeddable })}
- >
- {dashboardFilterNotificationActionStrings.getEditButtonTitle()}
-
-
-
+ {!disableEditbutton && (
+
+
+ editPanelAction.execute({ embeddable })}
+ >
+ {dashboardFilterNotificationActionStrings.getEditButtonTitle()}
+
+
+
+ )}
);
diff --git a/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover_contents.tsx b/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover_contents.tsx
index 9c21642aadeaa..9e2e5d3e2fa8c 100644
--- a/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover_contents.tsx
+++ b/src/plugins/dashboard/public/dashboard_actions/filters_notification_popover_contents.tsx
@@ -26,9 +26,13 @@ import { DashboardContainer } from '../dashboard_container/embeddable/dashboard_
export interface FiltersNotificationProps {
context: FiltersNotificationActionContext;
+ setDisableEditButton: (flag: boolean) => void;
}
-export function FiltersNotificationPopoverContents({ context }: FiltersNotificationProps) {
+export function FiltersNotificationPopoverContents({
+ context,
+ setDisableEditButton,
+}: FiltersNotificationProps) {
const { embeddable } = context;
const [isLoading, setIsLoading] = useState(true);
const [filters, setFilters] = useState([]);
@@ -53,6 +57,7 @@ export function FiltersNotificationPopoverContents({ context }: FiltersNotificat
const language = getAggregateQueryMode(embeddableQuery);
setQueryLanguage(language);
setQueryString(embeddableQuery[language as keyof AggregateQuery]);
+ setDisableEditButton(true);
}
}
setIsLoading(false);
diff --git a/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.tsx b/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.tsx
index 99fb8fbb8fe31..c8b70c043fcdc 100644
--- a/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.tsx
+++ b/src/plugins/unified_histogram/public/chart/hooks/use_chart_config_panel.tsx
@@ -67,7 +67,10 @@ export function useChartConfigPanel({
dataView={dataView}
adaptersTables={lensTablesAdapter}
updateAll={updateSuggestion}
- setIsFlyoutVisible={setIsFlyoutVisible}
+ closeFlyout={() => {
+ setIsFlyoutVisible(false);
+ }}
+ wrapInFlyout
datasourceId="textBased"
/>
);
diff --git a/src/plugins/unified_histogram/public/chart/utils/get_lens_attributes.test.ts b/src/plugins/unified_histogram/public/chart/utils/get_lens_attributes.test.ts
index b19c6db64c0e0..915c820ef7095 100644
--- a/src/plugins/unified_histogram/public/chart/utils/get_lens_attributes.test.ts
+++ b/src/plugins/unified_histogram/public/chart/utils/get_lens_attributes.test.ts
@@ -740,6 +740,43 @@ describe('getLensAttributes', () => {
`);
});
+ it('should return correct attributes for text based languages with adhoc dataview', () => {
+ const adHocDataview = {
+ ...dataView,
+ isPersisted: () => false,
+ } as DataView;
+ const lensAttrs = getLensAttributes({
+ title: 'test',
+ filters,
+ query,
+ dataView: adHocDataview,
+ timeInterval,
+ breakdownField: undefined,
+ suggestion: currentSuggestionMock,
+ });
+ expect(lensAttrs.attributes).toEqual({
+ state: expect.objectContaining({
+ adHocDataViews: {
+ 'index-pattern-with-timefield-id': {},
+ },
+ }),
+ references: [
+ {
+ id: 'index-pattern-with-timefield-id',
+ name: 'indexpattern-datasource-current-indexpattern',
+ type: 'index-pattern',
+ },
+ {
+ id: 'index-pattern-with-timefield-id',
+ name: 'indexpattern-datasource-layer-unifiedHistogram',
+ type: 'index-pattern',
+ },
+ ],
+ title: 'test',
+ visualizationType: 'lnsHeatmap',
+ });
+ });
+
it('should return suggestion title if no title is given', () => {
expect(
getLensAttributes({
diff --git a/src/plugins/unified_histogram/public/chart/utils/get_lens_attributes.ts b/src/plugins/unified_histogram/public/chart/utils/get_lens_attributes.ts
index 32c2d19a3a251..1e9b58a8e53c2 100644
--- a/src/plugins/unified_histogram/public/chart/utils/get_lens_attributes.ts
+++ b/src/plugins/unified_histogram/public/chart/utils/get_lens_attributes.ts
@@ -205,6 +205,13 @@ export const getLensAttributes = ({
filters,
query,
visualization,
+ ...(dataView &&
+ dataView.id &&
+ !dataView.isPersisted() && {
+ adHocDataViews: {
+ [dataView.id]: dataView.toSpec(false),
+ },
+ }),
},
visualizationType: suggestion ? suggestion.visualizationId : 'lnsXY',
} as TypedLensByValueInput['attributes'];
diff --git a/x-pack/plugins/enterprise_search/common/connectors/connectors.ts b/x-pack/plugins/enterprise_search/common/connectors/connectors.ts
index 34562ed68f36a..30b8186c0053d 100644
--- a/x-pack/plugins/enterprise_search/common/connectors/connectors.ts
+++ b/x-pack/plugins/enterprise_search/common/connectors/connectors.ts
@@ -58,6 +58,16 @@ export const CONNECTOR_DEFINITIONS: ConnectorServerSideDefinition[] = [
}),
serviceType: 'google_cloud_storage',
},
+ {
+ iconPath: 'google_drive.svg',
+ isBeta: true,
+ isNative: false,
+ keywords: ['google', 'drive', 'connector'],
+ name: i18n.translate('xpack.enterpriseSearch.content.nativeConnectors.googleDrive.name', {
+ defaultMessage: 'Google Drive',
+ }),
+ serviceType: 'google_drive',
+ },
{
iconPath: 'mongodb.svg',
isBeta: false,
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts
index 0f1603ea4fe0d..24b9d342f52d4 100644
--- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts
@@ -43,6 +43,12 @@ export const CONNECTORS_DICT: Record = {
externalDocsUrl: 'https://cloud.google.com/storage/docs',
icon: CONNECTOR_ICONS.google_cloud_storage,
},
+ google_drive: {
+ docsUrl: docLinks.connectorsGoogleDrive,
+ externalAuthDocsUrl: 'https://cloud.google.com/iam/docs/service-account-overview',
+ externalDocsUrl: 'https://developers.google.com/drive',
+ icon: CONNECTOR_ICONS.google_drive,
+ },
jira: {
docsUrl: docLinks.connectorsJira,
externalAuthDocsUrl: '',
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts
index 2861c8032e0a3..c61deecb811d0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/doc_links/doc_links.ts
@@ -68,6 +68,7 @@ class DocLinks {
public connectorsContentExtraction: string;
public connectorsDropbox: string;
public connectorsGoogleCloudStorage: string;
+ public connectorsGoogleDrive: string;
public connectorsJira: string;
public connectorsMicrosoftSQL: string;
public connectorsMongoDB: string;
@@ -223,6 +224,7 @@ class DocLinks {
this.connectorsClients = '';
this.connectorsDropbox = '';
this.connectorsGoogleCloudStorage = '';
+ this.connectorsGoogleDrive = '';
this.connectorsJira = '';
this.connectorsMicrosoftSQL = '';
this.connectorsMongoDB = '';
@@ -380,6 +382,7 @@ class DocLinks {
this.connectorsDropbox = docLinks.links.enterpriseSearch.connectorsDropbox;
this.connectorsGoogleCloudStorage =
docLinks.links.enterpriseSearch.connectorsGoogleCloudStorage;
+ this.connectorsGoogleDrive = docLinks.links.enterpriseSearch.connectorsGoogleDrive;
this.connectorsJira = docLinks.links.enterpriseSearch.connectorsJira;
this.connectorsMicrosoftSQL = docLinks.links.enterpriseSearch.connectorsMicrosoftSQL;
this.connectorsMongoDB = docLinks.links.enterpriseSearch.connectorsMongoDB;
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts b/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts
index cb7871bbe9f02..5533e6e5d3d3c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/icons/connector_icons.ts
@@ -11,6 +11,7 @@ import custom from '../../../assets/source_icons/custom.svg';
import dropbox from '../../../assets/source_icons/dropbox.svg';
import github from '../../../assets/source_icons/github.svg';
import google_cloud_storage from '../../../assets/source_icons/google_cloud_storage.svg';
+import google_drive from '../../../assets/source_icons/google_drive.svg';
import jira_cloud from '../../../assets/source_icons/jira_cloud.svg';
import mongodb from '../../../assets/source_icons/mongodb.svg';
import microsoft_sql from '../../../assets/source_icons/mssql.svg';
@@ -31,6 +32,7 @@ export const CONNECTOR_ICONS = {
dropbox,
github,
google_cloud_storage,
+ google_drive,
jira_cloud,
microsoft_sql,
mongodb,
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 0eb71d6f6e1d7..65ad607a91da5 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
@@ -51,7 +51,8 @@ export function getEditLensConfiguration(
attributes,
dataView,
updateAll,
- setIsFlyoutVisible,
+ closeFlyout,
+ wrapInFlyout,
datasourceId,
adaptersTables,
}: EditLensConfigurationProps) => {
@@ -88,15 +89,38 @@ export function getEditLensConfiguration(
const lensStore: LensRootStore = makeConfigureStore(storeDeps, {
lens: getPreloadedState(storeDeps) as LensAppState,
} as unknown as PreloadedState);
- const closeFlyout = () => {
- setIsFlyoutVisible?.(false);
+
+ const getWrapper = (children: JSX.Element) => {
+ if (wrapInFlyout) {
+ return (
+ {
+ closeFlyout?.();
+ }}
+ aria-labelledby={i18n.translate('xpack.lens.config.editLabel', {
+ defaultMessage: 'Edit configuration',
+ })}
+ size="s"
+ hideCloseButton
+ css={css`
+ background: none;
+ `}
+ >
+ {children}
+
+ );
+ } else {
+ return children;
+ }
};
const configPanelProps = {
attributes,
dataView,
updateAll,
- setIsFlyoutVisible,
+ closeFlyout,
datasourceId,
adaptersTables,
coreStart,
@@ -105,25 +129,10 @@ export function getEditLensConfiguration(
datasourceMap,
};
- return (
-
-
-
-
-
+ return getWrapper(
+
+
+
);
};
}
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 dc05ff1577382..3a14b26f5d9cc 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
@@ -120,22 +120,22 @@ describe('LensEditConfigurationFlyout', () => {
startDependencies,
visualizationMap,
datasourceMap,
- setIsFlyoutVisible: jest.fn(),
+ closeFlyout: jest.fn(),
datasourceId: 'testDatasource',
} as unknown as EditConfigPanelProps;
}
- it('should call the setIsFlyout callback if collapse button is clicked', async () => {
- const setIsFlyoutVisibleSpy = jest.fn();
+ it('should call the closeFlyout callback if collapse button is clicked', async () => {
+ const closeFlyoutSpy = jest.fn();
const props = getDefaultProps();
const newProps = {
...props,
- setIsFlyoutVisible: setIsFlyoutVisibleSpy,
+ closeFlyout: closeFlyoutSpy,
};
const { instance } = await prepareAndMountComponent(newProps);
expect(instance.find(EuiFlyoutBody).exists()).toBe(true);
instance.find('[data-test-subj="collapseFlyoutButton"]').at(1).simulate('click');
- expect(setIsFlyoutVisibleSpy).toHaveBeenCalled();
+ expect(closeFlyoutSpy).toHaveBeenCalled();
});
it('should compute the frame public api correctly', async () => {
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 9fe486c58048a..98add346d114d 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
@@ -43,7 +43,8 @@ export interface EditConfigPanelProps {
startDependencies: LensPluginStartDependencies;
visualizationMap: VisualizationMap;
datasourceMap: DatasourceMap;
- setIsFlyoutVisible?: (flag: boolean) => void;
+ closeFlyout?: () => void;
+ wrapInFlyout?: boolean;
datasourceId: 'formBased' | 'textBased';
adaptersTables?: Record;
}
@@ -57,7 +58,7 @@ export function LensEditConfigurationFlyout({
datasourceMap,
datasourceId,
updateAll,
- setIsFlyoutVisible,
+ closeFlyout,
adaptersTables,
}: EditConfigPanelProps) {
const currentDataViewId = dataView.id ?? '';
@@ -112,10 +113,6 @@ export function LensEditConfigurationFlyout({
};
}, [activeData, dataViews, datasourceLayers, dateRange]);
- const closeFlyout = () => {
- setIsFlyoutVisible?.(false);
- };
-
const layerPanelsProps = {
framePublicAPI,
datasourceMap,
diff --git a/x-pack/plugins/lens/public/async_services.ts b/x-pack/plugins/lens/public/async_services.ts
index d4c6fe5be8dcd..7bbfaf415db03 100644
--- a/x-pack/plugins/lens/public/async_services.ts
+++ b/x-pack/plugins/lens/public/async_services.ts
@@ -50,3 +50,4 @@ export * from './app_plugin/save_modal_container';
export * from './chart_info_api';
export * from './trigger_actions/open_in_discover_helpers';
+export * from './trigger_actions/open_lens_config/helpers';
diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx
index a5b1dbc957ef9..c690f875debf5 100644
--- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx
+++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx
@@ -18,6 +18,7 @@ import {
AggregateQuery,
TimeRange,
isOfQueryType,
+ getAggregateQueryMode,
} from '@kbn/es-query';
import type { PaletteOutput } from '@kbn/coloring';
import {
@@ -127,8 +128,11 @@ import {
} from '../app_plugin/get_application_user_messages';
import { MessageList } from '../editor_frame_service/editor_frame/workspace_panel/message_list';
import type { DocumentToExpressionReturnType } from '../editor_frame_service/editor_frame';
+import type { TypedLensByValueInput } from './embeddable_component';
+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';
export type LensSavedObjectAttributes = Omit;
@@ -718,6 +722,70 @@ export class Embeddable
return this.fullAttributes;
}
+ public isTextBasedLanguage() {
+ if (!this.savedVis) {
+ return;
+ }
+ const query = this.savedVis.state.query;
+ return !isOfQueryType(query);
+ }
+
+ public getTextBasedLanguage(): string | undefined {
+ if (!this.isTextBasedLanguage() || !this.savedVis?.state.query) {
+ return;
+ }
+ const query = this.savedVis?.state.query as unknown as AggregateQuery;
+ const language = getAggregateQueryMode(query);
+ return String(language).toUpperCase();
+ }
+
+ async updateVisualization(datasourceState: unknown, visualizationState: unknown) {
+ const viz = this.savedVis;
+ const datasourceId = (this.activeDatasourceId ??
+ 'formBased') as EditLensConfigurationProps['datasourceId'];
+ if (viz?.state) {
+ const attrs = {
+ ...viz,
+ state: {
+ ...viz.state,
+ visualization: visualizationState,
+ datasourceStates: {
+ ...viz.state.datasourceStates,
+ [datasourceId]: datasourceState,
+ },
+ },
+ };
+ this.updateInput({ attributes: attrs });
+ }
+ }
+
+ async openConfingPanel(startDependencies: LensPluginStartDependencies) {
+ const { getEditLensConfiguration } = await import('../async_services');
+ const Component = getEditLensConfiguration(
+ this.deps.coreStart,
+ startDependencies,
+ this.deps.visualizationMap,
+ this.deps.datasourceMap
+ );
+
+ const datasourceId = (this.activeDatasourceId ??
+ 'formBased') as EditLensConfigurationProps['datasourceId'];
+ const attributes = this.savedVis as TypedLensByValueInput['attributes'];
+ const dataView = this.dataViews[0];
+ if (attributes) {
+ return (
+
+ );
+ }
+ return null;
+ }
+
async initializeSavedVis(input: LensEmbeddableInput) {
const unwrapResult: LensUnwrapResult | false = await this.deps.attributeService
.unwrapAttributes(input)
@@ -1346,7 +1414,7 @@ export class Embeddable
this.updateOutput({
defaultTitle: this.savedVis.title,
defaultDescription: this.savedVis.description,
- editable: this.getIsEditable(),
+ editable: this.getIsEditable() && !this.isTextBasedLanguage(),
title,
description,
editPath: getEditPath(savedObjectId),
diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts
index 1a195183142c3..3f2559ce634be 100644
--- a/x-pack/plugins/lens/public/plugin.ts
+++ b/x-pack/plugins/lens/public/plugin.ts
@@ -104,6 +104,7 @@ import type {
} from './types';
import { getLensAliasConfig } from './vis_type_alias';
import { createOpenInDiscoverAction } from './trigger_actions/open_in_discover_action';
+import { ConfigureInLensPanelAction } from './trigger_actions/open_lens_config/action';
import { visualizeFieldAction } from './trigger_actions/visualize_field_actions';
import { visualizeTSVBAction } from './trigger_actions/visualize_tsvb_actions';
import { visualizeAggBasedVisAction } from './trigger_actions/visualize_agg_based_vis_actions';
@@ -589,6 +590,13 @@ export class LensPlugin {
visualizeAggBasedVisAction(core.application)
);
+ const editInLensAction = new ConfigureInLensPanelAction(
+ startDependencies,
+ core.overlays,
+ core.theme
+ );
+ startDependencies.uiActions.addTriggerAction('CONTEXT_MENU_TRIGGER', editInLensAction);
+
const discoverLocator = startDependencies.share?.url.locators.get('DISCOVER_APP_LOCATOR');
if (discoverLocator) {
startDependencies.uiActions.addTriggerAction(
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 cb35f16f0e8c5..60d15f532d3d4 100644
--- a/x-pack/plugins/lens/public/state_management/lens_slice.ts
+++ b/x-pack/plugins/lens/public/state_management/lens_slice.ts
@@ -30,6 +30,20 @@ import { getVisualizeFieldSuggestions } from '../editor_frame_service/editor_fra
import type { FramePublicAPI, LensEditContextMapping, LensEditEvent } from '../types';
import { selectDataViews, selectFramePublicAPI } from './selectors';
import { onDropForVisualization } from '../editor_frame_service/editor_frame/config_panel/buttons/drop_targets_utils';
+import type { LensAppServices } from '../app_plugin/types';
+
+const getQueryFromContext = (
+ context: VisualizeFieldContext | VisualizeEditorContext,
+ data: LensAppServices['data']
+) => {
+ if ('searchQuery' in context && context.searchQuery) {
+ return context.searchQuery;
+ }
+ if ('query' in context && context.query) {
+ return context.query;
+ }
+ return data.query.queryString.getQuery();
+};
export const initialState: LensAppState = {
persistedDoc: undefined,
@@ -93,16 +107,16 @@ export const getPreloadedState = ({
};
}
+ const query = !initialContext
+ ? data.query.queryString.getDefaultQuery()
+ : getQueryFromContext(initialContext, data);
+
const state = {
...initialState,
isLoading: true,
// Do not use app-specific filters from previous app,
// only if Lens was opened with the intention to visualize a field (e.g. coming from Discover)
- query: !initialContext
- ? data.query.queryString.getDefaultQuery()
- : 'searchQuery' in initialContext && initialContext.searchQuery
- ? initialContext.searchQuery
- : (data.query.queryString.getQuery() as Query),
+ query: query as Query,
filters: !initialContext
? data.query.filterManager.getGlobalFilters()
: 'searchFilters' in initialContext && initialContext.searchFilters
diff --git a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_helpers.ts b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_helpers.ts
index 15773ccbbd1f8..bd3a10536d343 100644
--- a/x-pack/plugins/lens/public/trigger_actions/open_in_discover_helpers.ts
+++ b/x-pack/plugins/lens/public/trigger_actions/open_in_discover_helpers.ts
@@ -10,8 +10,7 @@ import type { IEmbeddable } from '@kbn/embeddable-plugin/public';
import type { DataViewsService } from '@kbn/data-views-plugin/public';
import type { LocatorPublic } from '@kbn/share-plugin/public';
import type { SerializableRecord } from '@kbn/utility-types';
-import type { Embeddable } from '../embeddable';
-import { DOC_TYPE } from '../../common/constants';
+import { isLensEmbeddable } from './utils';
interface DiscoverAppLocatorParams extends SerializableRecord {
timeRange?: TimeRange;
@@ -33,10 +32,6 @@ interface Context {
timeFieldName?: string;
}
-export function isLensEmbeddable(embeddable: IEmbeddable): embeddable is Embeddable {
- return embeddable.type === DOC_TYPE;
-}
-
export async function isCompatible({ hasDiscoverAccess, embeddable }: Context) {
if (!hasDiscoverAccess) return false;
try {
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
new file mode 100644
index 0000000000000..6c3d98fb3637d
--- /dev/null
+++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.test.tsx
@@ -0,0 +1,104 @@
+/*
+ * 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 React from 'react';
+import type { IEmbeddable } from '@kbn/embeddable-plugin/public';
+import { ActionExecutionContext } from '@kbn/ui-actions-plugin/public';
+import { overlayServiceMock } from '@kbn/core-overlays-browser-mocks';
+import { themeServiceMock } from '@kbn/core-theme-browser-mocks';
+import type { LensPluginStartDependencies } from '../../plugin';
+import { createMockStartDependencies } from '../../editor_frame_service/mocks';
+import { DOC_TYPE } from '../../../common/constants';
+import { ConfigureInLensPanelAction } from './action';
+
+describe('open config panel action', () => {
+ const overlays = overlayServiceMock.createStartContract();
+ const theme = themeServiceMock.createStartContract();
+ const mockStartDependencies =
+ createMockStartDependencies() as unknown as LensPluginStartDependencies;
+ describe('compatibility check', () => {
+ it('is incompatible with non-lens embeddables', async () => {
+ const embeddable = {
+ type: 'NOT_LENS',
+ isTextBasedLanguage: () => true,
+ } 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 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,
+ isTextBasedLanguage: () => true,
+ } as unknown as IEmbeddable;
+ const configurablePanelAction = new ConfigureInLensPanelAction(
+ mockStartDependencies,
+ overlays,
+ theme
+ );
+
+ const isCompatible = await configurablePanelAction.isCompatible({
+ embeddable,
+ } as ActionExecutionContext<{ embeddable: IEmbeddable }>);
+
+ expect(isCompatible).toBeTruthy();
+ });
+ });
+ describe('execution', () => {
+ it('opens flyout when executed', async () => {
+ const embeddable = {
+ type: DOC_TYPE,
+ isTextBasedLanguage: () => true,
+ openConfingPanel: jest.fn().mockResolvedValue(Lens Config Panel Component),
+ getRoot: () => {
+ return {
+ openOverlay: jest.fn(),
+ clearOverlays: jest.fn(),
+ };
+ },
+ } as unknown as IEmbeddable;
+ const configurablePanelAction = new ConfigureInLensPanelAction(
+ mockStartDependencies,
+ overlays,
+ theme
+ );
+ const spy = jest.spyOn(overlays, 'openFlyout');
+
+ await configurablePanelAction.execute({
+ embeddable,
+ } as ActionExecutionContext<{ embeddable: IEmbeddable }>);
+
+ expect(spy).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.tsx b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.tsx
new file mode 100644
index 0000000000000..376cae58d1339
--- /dev/null
+++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/action.tsx
@@ -0,0 +1,59 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import type { IEmbeddable } from '@kbn/embeddable-plugin/public';
+import { OverlayStart, ThemeServiceStart } from '@kbn/core/public';
+import { Action } from '@kbn/ui-actions-plugin/public';
+import type { LensPluginStartDependencies } from '../../plugin';
+import { isLensEmbeddable } from '../utils';
+
+const ACTION_CONFIGURE_IN_LENS = 'ACTION_CONFIGURE_IN_LENS';
+
+interface Context {
+ embeddable: IEmbeddable;
+}
+
+export const getConfigureLensHelpersAsync = async () => await import('../../async_services');
+
+export class ConfigureInLensPanelAction implements Action {
+ public type = ACTION_CONFIGURE_IN_LENS;
+ public id = ACTION_CONFIGURE_IN_LENS;
+ public order = 50;
+
+ constructor(
+ protected readonly startDependencies: LensPluginStartDependencies,
+ protected readonly overlays: OverlayStart,
+ protected readonly theme: ThemeServiceStart
+ ) {}
+
+ public getDisplayName({ embeddable }: Context): string {
+ const language = isLensEmbeddable(embeddable) ? embeddable.getTextBasedLanguage() : undefined;
+ return i18n.translate('xpack.lens.app.editVisualizationLabel', {
+ defaultMessage: 'Edit {lang} visualization',
+ values: { lang: language },
+ });
+ }
+
+ public getIconType() {
+ return 'pencil';
+ }
+
+ public async isCompatible({ embeddable }: Context) {
+ const { isActionCompatible } = await getConfigureLensHelpersAsync();
+ return isActionCompatible(embeddable);
+ }
+
+ public async execute({ embeddable }: Context) {
+ const { executeAction } = await getConfigureLensHelpersAsync();
+ return executeAction({
+ embeddable,
+ startDependencies: this.startDependencies,
+ overlays: this.overlays,
+ theme: this.theme,
+ });
+ }
+}
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
new file mode 100644
index 0000000000000..985424d18e1c2
--- /dev/null
+++ b/x-pack/plugins/lens/public/trigger_actions/open_lens_config/helpers.ts
@@ -0,0 +1,70 @@
+/*
+ * 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 React from 'react';
+import type { IEmbeddable } from '@kbn/embeddable-plugin/public';
+import type { OverlayRef, OverlayStart, ThemeServiceStart } from '@kbn/core/public';
+import { toMountPoint } from '@kbn/kibana-react-plugin/public';
+import { IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
+import { isLensEmbeddable } from '../utils';
+import type { LensPluginStartDependencies } from '../../plugin';
+
+interface Context {
+ embeddable: IEmbeddable;
+ startDependencies: LensPluginStartDependencies;
+ overlays: OverlayStart;
+ theme: ThemeServiceStart;
+}
+
+interface TracksOverlays {
+ openOverlay: (ref: OverlayRef) => void;
+ clearOverlays: () => void;
+}
+
+function tracksOverlays(root: unknown): root is TracksOverlays {
+ return Boolean((root as TracksOverlays).openOverlay && (root as TracksOverlays).clearOverlays);
+}
+
+export async function isActionCompatible(embeddable: IEmbeddable) {
+ return Boolean(isLensEmbeddable(embeddable) && embeddable.isTextBasedLanguage());
+}
+
+export async function executeAction({ embeddable, startDependencies, overlays, theme }: Context) {
+ const isCompatibleAction = await isActionCompatible(embeddable);
+ if (!isCompatibleAction || !isLensEmbeddable(embeddable)) {
+ throw new IncompatibleActionError();
+ }
+ const rootEmbeddable = embeddable.getRoot();
+ const overlayTracker = tracksOverlays(rootEmbeddable) ? rootEmbeddable : undefined;
+ const ConfigPanel = await embeddable.openConfingPanel(startDependencies);
+ if (ConfigPanel) {
+ const handle = overlays.openFlyout(
+ toMountPoint(
+ React.cloneElement(ConfigPanel, {
+ closeFlyout: () => {
+ if (overlayTracker) overlayTracker.clearOverlays();
+ handle.close();
+ },
+ }),
+ {
+ theme$: theme.theme$,
+ }
+ ),
+ {
+ size: 's',
+ 'data-test-subj': 'customizeLens',
+ type: 'push',
+ hideCloseButton: true,
+ onClose: (overlayRef) => {
+ if (overlayTracker) overlayTracker.clearOverlays();
+ overlayRef.close();
+ },
+ outsideClickCloses: true,
+ }
+ );
+ overlayTracker?.openOverlay(handle);
+ }
+}
diff --git a/x-pack/plugins/lens/public/trigger_actions/utils.ts b/x-pack/plugins/lens/public/trigger_actions/utils.ts
new file mode 100644
index 0000000000000..527f1adcf7629
--- /dev/null
+++ b/x-pack/plugins/lens/public/trigger_actions/utils.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import type { IEmbeddable } from '@kbn/embeddable-plugin/public';
+import type { Embeddable } from '../embeddable';
+import { DOC_TYPE } from '../../common/constants';
+
+export function isLensEmbeddable(embeddable: IEmbeddable): embeddable is Embeddable {
+ return embeddable.type === DOC_TYPE;
+}
diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json
index 10517769418f7..9cb461d1f1491 100644
--- a/x-pack/plugins/lens/tsconfig.json
+++ b/x-pack/plugins/lens/tsconfig.json
@@ -78,7 +78,9 @@
"@kbn/core-notifications-browser-mocks",
"@kbn/core-saved-objects-utils-server",
"@kbn/core-lifecycle-browser-mocks",
- "@kbn/unified-field-list"
+ "@kbn/unified-field-list",
+ "@kbn/core-overlays-browser-mocks",
+ "@kbn/core-theme-browser-mocks"
],
"exclude": [
"target/**/*",
diff --git a/x-pack/test/api_integration/apis/ml/jobs/update_groups.ts b/x-pack/test/api_integration/apis/ml/jobs/update_groups.ts
index 1702388eef4c6..20ebb75a219e8 100644
--- a/x-pack/test/api_integration/apis/ml/jobs/update_groups.ts
+++ b/x-pack/test/api_integration/apis/ml/jobs/update_groups.ts
@@ -57,7 +57,8 @@ export default ({ getService }: FtrProviderContext) => {
{ id: 'new_group', jobIds: [SINGLE_METRIC_JOB_CONFIG.job_id], calendarIds: [] },
];
- describe('update groups', function () {
+ // Failing: See https://github.com/elastic/kibana/issues/161324
+ describe.skip('update groups', function () {
before(async () => {
await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote');
await ml.testResources.setKibanaTimeZoneToUTC();