[TextBased] Allow inline editing from dashboards (#161146)

## Summary

Part of https://github.com/elastic/kibana/issues/158802

For panels created from Discover with text based languages, not navigate
to Lens but open a flyout instead.


![sql](489402ac-dcdb-468b-89b9-a84f4c4f2ca5)


Follow up PR: 
- Remove the SQL option from Lens dataview picker and move the FTs in
Discover/Dashboard

Note:
- Changing the query on the dashboard level is going to be added in 8.11

### Checklist

- [ ] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [ ] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [ ] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Stratoula Kalafateli 2023-07-07 11:20:45 +03:00 committed by GitHub
parent 3495720e42
commit fde953907c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 467 additions and 69 deletions

View file

@ -83,7 +83,7 @@ pageLoadAssetSize:
kibanaUsageCollection: 16463
kibanaUtils: 79713
kubernetesSecurity: 77234
lens: 37000
lens: 38000
licenseManagement: 41817
licensing: 29004
lists: 22900

View file

@ -40,6 +40,7 @@ export function FiltersNotificationPopover({
}: FiltersNotificationProps) {
const { embeddable } = context;
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [disableEditbutton, setDisableEditButton] = useState(false);
return (
<EuiPopover
@ -57,26 +58,31 @@ export function FiltersNotificationPopover({
anchorPosition="upCenter"
>
<EuiPopoverTitle>{displayName}</EuiPopoverTitle>
<FiltersNotificationPopoverContents context={context} />
<FiltersNotificationPopoverContents
context={context}
setDisableEditButton={setDisableEditButton}
/>
<EuiPopoverFooter>
<EuiFlexGroup
gutterSize="s"
alignItems="center"
justifyContent="flexEnd"
responsive={false}
wrap={true}
>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj={'filtersNotificationModal__editButton'}
size="s"
fill
onClick={() => editPanelAction.execute({ embeddable })}
>
{dashboardFilterNotificationActionStrings.getEditButtonTitle()}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
{!disableEditbutton && (
<EuiFlexGroup
gutterSize="s"
alignItems="center"
justifyContent="flexEnd"
responsive={false}
wrap={true}
>
<EuiFlexItem grow={false}>
<EuiButton
data-test-subj={'filtersNotificationModal__editButton'}
size="s"
fill
onClick={() => editPanelAction.execute({ embeddable })}
>
{dashboardFilterNotificationActionStrings.getEditButtonTitle()}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
)}
</EuiPopoverFooter>
</EuiPopover>
);

View file

@ -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<Filter[]>([]);
@ -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);

View file

@ -67,7 +67,10 @@ export function useChartConfigPanel({
dataView={dataView}
adaptersTables={lensTablesAdapter}
updateAll={updateSuggestion}
setIsFlyoutVisible={setIsFlyoutVisible}
closeFlyout={() => {
setIsFlyoutVisible(false);
}}
wrapInFlyout
datasourceId="textBased"
/>
);

View file

@ -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({

View file

@ -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'];

View file

@ -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<LensState>);
const closeFlyout = () => {
setIsFlyoutVisible?.(false);
const getWrapper = (children: JSX.Element) => {
if (wrapInFlyout) {
return (
<EuiFlyout
type="push"
ownFocus
onClose={() => {
closeFlyout?.();
}}
aria-labelledby={i18n.translate('xpack.lens.config.editLabel', {
defaultMessage: 'Edit configuration',
})}
size="s"
hideCloseButton
css={css`
background: none;
`}
>
{children}
</EuiFlyout>
);
} else {
return children;
}
};
const configPanelProps = {
attributes,
dataView,
updateAll,
setIsFlyoutVisible,
closeFlyout,
datasourceId,
adaptersTables,
coreStart,
@ -105,25 +129,10 @@ export function getEditLensConfiguration(
datasourceMap,
};
return (
<EuiFlyout
type="push"
ownFocus
onClose={closeFlyout}
aria-labelledby={i18n.translate('xpack.lens.config.editLabel', {
defaultMessage: 'Edit configuration',
})}
size="s"
className="lnsEditConfigurationFlyout"
css={css`
background: none;
`}
hideCloseButton
>
<Provider store={lensStore}>
<LensEditConfigurationFlyout {...configPanelProps} />
</Provider>
</EuiFlyout>
return getWrapper(
<Provider store={lensStore}>
<LensEditConfigurationFlyout {...configPanelProps} />
</Provider>
);
};
}

View file

@ -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 () => {

View file

@ -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<string, Datatable>;
}
@ -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,

View file

@ -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';

View file

@ -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<Document, 'savedObjectId' | 'type'>;
@ -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 (
<Component
attributes={attributes}
dataView={dataView}
updateAll={this.updateVisualization.bind(this)}
datasourceId={datasourceId}
adaptersTables={this.lensInspector.adapters.tables?.tables}
/>
);
}
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),

View file

@ -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(

View file

@ -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

View file

@ -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 {

View file

@ -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(<span>Lens Config Panel Component</span>),
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();
});
});
});

View file

@ -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<Context> {
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,
});
}
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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/**/*",