[Dashboard] Add filter details to panel settings (#162913)

This commit is contained in:
Catherine Liu 2023-08-17 12:36:10 -07:00 committed by GitHub
parent db1cec4c94
commit 3720270232
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 333 additions and 54 deletions

View file

@ -52,7 +52,11 @@ export function FiltersNotificationPopoverContents({
setFilters(embeddableFilters);
if (embeddableQuery) {
if (isOfQueryType(embeddableQuery)) {
setQueryString(embeddableQuery.query as string);
if (typeof embeddableQuery.query === 'string') {
setQueryString(embeddableQuery.query);
} else {
setQueryString(JSON.stringify(embeddableQuery.query, null, 2));
}
} else {
const language = getAggregateQueryMode(embeddableQuery);
setQueryLanguage(language);
@ -79,8 +83,7 @@ export function FiltersNotificationPopoverContents({
>
<EuiCodeBlock
language={queryLanguage}
paddingSize="none"
transparentBackground
paddingSize="s"
aria-labelledby={`${dashboardFilterNotificationActionStrings.getQueryTitle()}: ${queryString}`}
tabIndex={0} // focus so that keyboard controls will not skip over the code block
>

View file

@ -16,7 +16,7 @@
"contentManagement"
],
"optionalPlugins": ["savedObjectsTaggingOss", "usageCollection"],
"requiredBundles": ["savedObjects", "kibanaReact", "kibanaUtils"],
"requiredBundles": ["savedObjects", "kibanaReact", "kibanaUtils", "unifiedSearch"],
"extraPublicDirs": ["common"]
}
}

View file

@ -71,21 +71,23 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => {
const commonlyUsedRanges = core.uiSettings.get(UI_SETTINGS.TIMEPICKER_QUICK_RANGES);
const dateFormat = core.uiSettings.get(UI_SETTINGS.DATE_FORMAT);
const stateTransfer = embeddableStart.getStateTransfer();
const editPanel = new EditPanelAction(
embeddableStart.getEmbeddableFactory,
core.application,
stateTransfer,
containerContext?.getCurrentPath
);
const actions: PanelUniversalActions = {
customizePanel: new CustomizePanelAction(
core.overlays,
core.theme,
editPanel,
commonlyUsedRanges,
dateFormat
),
removePanel: new RemovePanelAction(),
editPanel: new EditPanelAction(
embeddableStart.getEmbeddableFactory,
core.application,
stateTransfer,
containerContext?.getCurrentPath
),
editPanel,
};
if (!hideInspector) actions.inspectPanel = new InspectPanelAction(inspector);
return actions;

View file

@ -15,6 +15,11 @@ import {
TIME_RANGE_EMBEDDABLE,
} from '../../../lib/test_samples/embeddables';
import { CustomTimeRangeBadge } from './custom_time_range_badge';
import { EditPanelAction } from '../edit_panel_action/edit_panel_action';
const editPanelAction = {
execute: jest.fn(),
} as unknown as EditPanelAction;
test(`badge is not compatible with embeddable that inherits from parent`, async () => {
const container = new TimeRangeContainer(
@ -40,6 +45,7 @@ test(`badge is not compatible with embeddable that inherits from parent`, async
const compatible = await new CustomTimeRangeBadge(
overlayServiceMock.createStartContract(),
themeServiceMock.createStartContract(),
editPanelAction,
[],
'MM YYYY'
).isCompatible({
@ -73,6 +79,7 @@ test(`badge is compatible with embeddable that has custom time range`, async ()
const compatible = await new CustomTimeRangeBadge(
overlayServiceMock.createStartContract(),
themeServiceMock.createStartContract(),
editPanelAction,
[],
'MM YYYY'
).isCompatible({
@ -105,6 +112,7 @@ test('Attempting to execute on incompatible embeddable throws an error', async (
const badge = await new CustomTimeRangeBadge(
overlayServiceMock.createStartContract(),
themeServiceMock.createStartContract(),
editPanelAction,
[],
'MM YYYY'
);

View file

@ -21,11 +21,13 @@ import {
} from '../../../lib/test_samples/embeddables/contact_card/contact_card_embeddable_factory';
import { HelloWorldContainer } from '../../../lib/test_samples/embeddables/hello_world_container';
import { embeddablePluginMock } from '../../../mocks';
import { EditPanelAction } from '../edit_panel_action/edit_panel_action';
let container: Container;
let embeddable: ContactCardEmbeddable;
const overlays = overlayServiceMock.createStartContract();
const theme = themeServiceMock.createStartContract();
const editPanelActionMock = { execute: jest.fn() } as unknown as EditPanelAction;
function createHelloWorldContainer(input = { id: '123', panels: {} }) {
const { setup, doStart } = embeddablePluginMock.createInstance();
@ -57,7 +59,7 @@ beforeAll(async () => {
});
test('execute should open flyout', async () => {
const customizePanelAction = new CustomizePanelAction(overlays, theme);
const customizePanelAction = new CustomizePanelAction(overlays, theme, editPanelActionMock);
const spy = jest.spyOn(overlays, 'openFlyout');
await customizePanelAction.execute({ embeddable });

View file

@ -9,11 +9,19 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { TimeRange } from '@kbn/es-query';
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import { createKibanaReactContext } from '@kbn/kibana-react-plugin/public';
import { OverlayStart, ThemeServiceStart } from '@kbn/core/public';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public';
import { IEmbeddable, Embeddable, EmbeddableInput, EmbeddableOutput } from '../../..';
import { core } from '../../../kibana_services';
import {
IEmbeddable,
Embeddable,
EmbeddableInput,
EmbeddableOutput,
EditPanelAction,
} from '../../..';
import { ViewMode, CommonlyUsedRange } from '../../../lib/types';
import { tracksOverlays } from '../track_overlays';
import { CustomizePanelEditor } from './customize_panel_editor';
@ -52,6 +60,7 @@ export class CustomizePanelAction implements Action<CustomizePanelActionContext>
constructor(
protected readonly overlays: OverlayStart,
protected readonly theme: ThemeServiceStart,
protected readonly editPanel: EditPanelAction,
protected readonly commonlyUsedRanges?: CommonlyUsedRange[],
protected readonly dateFormat?: string
) {}
@ -99,19 +108,30 @@ export class CustomizePanelAction implements Action<CustomizePanelActionContext>
const rootEmbeddable = embeddable.getRoot();
const overlayTracker = tracksOverlays(rootEmbeddable) ? rootEmbeddable : undefined;
const { Provider: KibanaReactContextProvider } = createKibanaReactContext({
uiSettings: core.uiSettings,
});
const onEdit = () => {
this.editPanel.execute({ embeddable });
};
const handle = this.overlays.openFlyout(
toMountPoint(
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={this.isTimeRangeCompatible({ embeddable })}
dateFormat={this.dateFormat}
commonlyUsedRanges={this.commonlyUsedRanges}
onClose={() => {
if (overlayTracker) overlayTracker.clearOverlays();
handle.close();
}}
/>,
{ theme$: this.theme.theme$ }
<KibanaReactContextProvider>
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={this.isTimeRangeCompatible({ embeddable })}
dateFormat={this.dateFormat}
commonlyUsedRanges={this.commonlyUsedRanges}
onClose={() => {
if (overlayTracker) overlayTracker.clearOverlays();
handle.close();
}}
onEdit={onEdit}
/>
</KibanaReactContextProvider>,
{ theme: this.theme, i18n: core.i18n }
),
{
size: 's',
@ -120,6 +140,7 @@ export class CustomizePanelAction implements Action<CustomizePanelActionContext>
if (overlayTracker) overlayTracker.clearOverlays();
overlayRef.close();
},
maxWidth: true,
}
);
overlayTracker?.openOverlay(handle);

View file

@ -23,6 +23,7 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiSuperDatePicker,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { TimeRange } from '@kbn/es-query';
@ -31,7 +32,14 @@ import { FormattedMessage } from '@kbn/i18n-react';
import { TimeRangeInput } from './customize_panel_action';
import { canInheritTimeRange } from './can_inherit_time_range';
import { doesInheritTimeRange } from './does_inherit_time_range';
import { IEmbeddable, Embeddable, CommonlyUsedRange, ViewMode } from '../../../lib';
import {
IEmbeddable,
Embeddable,
CommonlyUsedRange,
ViewMode,
isFilterableEmbeddable,
} from '../../../lib';
import { FiltersDetails } from './filters_details';
type PanelSettings = {
title?: string;
@ -46,10 +54,11 @@ interface CustomizePanelProps {
dateFormat?: string;
commonlyUsedRanges?: CommonlyUsedRange[];
onClose: () => void;
onEdit: () => void;
}
export const CustomizePanelEditor = (props: CustomizePanelProps) => {
const { onClose, embeddable, dateFormat, timeRangeCompatible } = props;
const { onClose, embeddable, dateFormat, timeRangeCompatible, onEdit } = props;
const editMode = embeddable.getInput().viewMode === ViewMode.EDIT;
const [hideTitle, setHideTitle] = useState(embeddable.getInput().hidePanelTitles);
const [panelDescription, setPanelDescription] = useState(
@ -259,6 +268,17 @@ export const CustomizePanelEditor = (props: CustomizePanelProps) => {
);
};
const renderFilterDetails = () => {
if (!isFilterableEmbeddable(embeddable)) return null;
return (
<>
<EuiSpacer size="m" />
<FiltersDetails onEdit={onEdit} embeddable={embeddable} editMode={editMode} />
</>
);
};
return (
<>
<EuiFlyoutHeader hasBorder>
@ -275,6 +295,7 @@ export const CustomizePanelEditor = (props: CustomizePanelProps) => {
<EuiForm>
{renderCustomTitleComponent()}
{renderCustomTimeRangeComponent()}
{renderFilterDetails()}
</EuiForm>
</EuiFlyoutBody>
<EuiFlyoutFooter>

View file

@ -0,0 +1,157 @@
/*
* 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 React, { useMemo, useState } from 'react';
import useMount from 'react-use/lib/useMount';
import {
EuiButtonEmpty,
EuiCodeBlock,
EuiFlexGroup,
EuiFormRow,
EuiSkeletonText,
} from '@elastic/eui';
import { FilterItems } from '@kbn/unified-search-plugin/public';
import {
type AggregateQuery,
type Filter,
getAggregateQueryMode,
isOfQueryType,
} from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { DataView } from '@kbn/data-views-plugin/common';
import { IEmbeddable } from '../../../lib/embeddables';
import { isFilterableEmbeddable } from '../../../lib/filterable_embeddable';
export const filterDetailsActionStrings = {
getQueryTitle: () =>
i18n.translate('embeddableApi.panel.filters.queryTitle', {
defaultMessage: 'Query',
}),
getFiltersTitle: () =>
i18n.translate('embeddableApi.panel.filters.filtersTitle', {
defaultMessage: 'Filters',
}),
};
interface FiltersDetailsProps {
embeddable: IEmbeddable;
editMode: boolean;
onEdit: () => void;
}
export function FiltersDetails({ embeddable, editMode, onEdit }: FiltersDetailsProps) {
const [isLoading, setIsLoading] = useState(true);
const [filters, setFilters] = useState<Filter[]>([]);
const [queryString, setQueryString] = useState<string>('');
const [queryLanguage, setQueryLanguage] = useState<'sql' | 'esql' | undefined>();
const [disableEditbutton, setDisableEditButton] = useState(false);
const dataViews = useMemo(
() => (embeddable.getOutput() as { indexPatterns?: DataView[] }).indexPatterns || [],
[embeddable]
);
useMount(() => {
if (!isFilterableEmbeddable(embeddable)) {
setIsLoading(false);
return;
}
Promise.all([embeddable.getFilters(), embeddable.getQuery()]).then(
([embeddableFilters, embeddableQuery]) => {
setFilters(embeddableFilters);
if (embeddableQuery) {
if (isOfQueryType(embeddableQuery)) {
if (typeof embeddableQuery.query === 'string') {
setQueryString(embeddableQuery.query);
} else {
setQueryString(JSON.stringify(embeddableQuery.query, null, 2));
}
} else {
const language = getAggregateQueryMode(embeddableQuery);
setQueryLanguage(language);
setQueryString(embeddableQuery[language as keyof AggregateQuery]);
setDisableEditButton(true);
}
}
setIsLoading(false);
}
);
});
return (
<EuiSkeletonText isLoading={isLoading} lines={3}>
{queryString !== '' && (
<EuiFormRow
label={filterDetailsActionStrings.getQueryTitle()}
display="rowCompressed"
labelAppend={
editMode && !disableEditbutton ? (
<EuiButtonEmpty
size="xs"
data-test-subj="customizePanelEditQueryButton"
onClick={onEdit}
aria-label={i18n.translate(
'embeddableApi.customizePanel.flyout.optionsMenuForm.editQueryButtonAriaLabel',
{
defaultMessage: 'Edit query',
}
)}
>
<FormattedMessage
id="embeddableApi.customizePanel.flyout.optionsMenuForm.editQueryButtonLabel"
defaultMessage="Edit"
/>
</EuiButtonEmpty>
) : null
}
>
<EuiCodeBlock
language={queryLanguage}
paddingSize="s"
fontSize="s"
aria-labelledby={`${filterDetailsActionStrings.getQueryTitle()}: ${queryString}`}
tabIndex={0} // focus so that keyboard controls will not skip over the code block
>
{queryString}
</EuiCodeBlock>
</EuiFormRow>
)}
{filters.length > 0 && (
<EuiFormRow
label={filterDetailsActionStrings.getFiltersTitle()}
labelAppend={
editMode && !disableEditbutton ? (
<EuiButtonEmpty
size="xs"
data-test-subj="customizePanelEditFiltersButton"
onClick={onEdit}
aria-label={i18n.translate(
'embeddableApi.customizePanel.flyout.optionsMenuForm.editFiltersButtonAriaLabel',
{
defaultMessage: 'Edit filters',
}
)}
>
<FormattedMessage
id="embeddableApi.customizePanel.flyout.optionsMenuForm.editFiltersButtonLabel"
defaultMessage="Edit"
/>
</EuiButtonEmpty>
) : null
}
>
<EuiFlexGroup wrap={true} gutterSize="xs">
<FilterItems filters={filters} indexPatterns={dataViews} readOnly={true} />
</EuiFlexGroup>
</EuiFormRow>
)}
</EuiSkeletonText>
);
}

View file

@ -12,8 +12,6 @@ import * as kibanaServices from '../kibana_services';
import { ErrorEmbeddable, IEmbeddable } from '../lib';
import { useEmbeddablePanel } from './use_embeddable_panel';
jest.mock('../kibana_services');
describe('useEmbeddablePanel', () => {
afterEach(() => {
jest.resetAllMocks();

View file

@ -39,6 +39,10 @@ export class TimeRangeContainer extends Container<
super(initialInput, { embeddableLoaded: {} }, getFactory, parent);
}
public getAllDataViews() {
return [];
}
public getInheritedInput() {
return { timeRange: this.input.timeRange };
}

View file

@ -56,7 +56,7 @@ import {
import { getAllMigrations } from '../common/lib/get_all_migrations';
import { setTheme } from './services';
import { setKibanaServices } from './kibana_services';
import { CustomTimeRangeBadge } from './embeddable_panel/panel_actions';
import { CustomTimeRangeBadge, EditPanelAction } from './embeddable_panel/panel_actions';
export interface EmbeddableSetupDependencies {
uiActions: UiActionsSetup;
@ -153,15 +153,6 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
const dateFormat = uiSettings.get(UI_SETTINGS.DATE_FORMAT);
const commonlyUsedRanges = uiSettings.get(UI_SETTINGS.TIMEPICKER_QUICK_RANGES);
const timeRangeBadge = new CustomTimeRangeBadge(
overlays,
theme,
commonlyUsedRanges,
dateFormat
);
uiActions.addTriggerAction(PANEL_BADGE_TRIGGER, timeRangeBadge);
this.appListSubscription = core.application.applications$.subscribe((appList) => {
this.appList = appList;
});
@ -173,6 +164,22 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
);
this.isRegistryReady = true;
const editPanel = new EditPanelAction(
this.getEmbeddableFactory,
core.application,
this.stateTransferService
);
const timeRangeBadge = new CustomTimeRangeBadge(
overlays,
theme,
editPanel,
commonlyUsedRanges,
dateFormat
);
uiActions.addTriggerAction(PANEL_BADGE_TRIGGER, timeRangeBadge);
const commonContract: CommonEmbeddableStartContract = {
getEmbeddableFactory: this
.getEmbeddableFactory as unknown as CommonEmbeddableStartContract['getEmbeddableFactory'],

View file

@ -20,10 +20,15 @@ import {
TIME_RANGE_EMBEDDABLE,
} from '../lib/test_samples';
import { CustomizePanelEditor } from '../embeddable_panel/panel_actions/customize_panel_action/customize_panel_editor';
import { embeddablePluginMock } from '../mocks';
import { AggregateQuery, Filter, Query } from '@kbn/es-query';
let container: TimeRangeContainer;
let embeddable: TimeRangeEmbeddable;
const mockGetFilters = jest.fn(async () => [] as Filter[]);
const mockGetQuery = jest.fn(async () => undefined as Query | AggregateQuery | undefined);
beforeEach(async () => {
const { doStart, setup } = testPlugin(coreMock.createSetup(), coreMock.createStart());
@ -46,16 +51,25 @@ beforeEach(async () => {
description: 'This might be a neat line chart',
viewMode: ViewMode.EDIT,
});
if (isErrorEmbeddable(timeRangeEmbeddable)) {
throw new Error('Error creating new hello world embeddable');
} else {
embeddable = timeRangeEmbeddable;
embeddable = embeddablePluginMock.mockFilterableEmbeddable(timeRangeEmbeddable, {
getFilters: mockGetFilters,
getQuery: mockGetQuery,
});
}
});
test('Value is initialized with the embeddables title', async () => {
const component = mountWithIntl(
<CustomizePanelEditor embeddable={embeddable} timeRangeCompatible={true} onClose={() => {}} />
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={true}
onClose={() => {}}
onEdit={() => {}}
/>
);
const titleField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input');
@ -69,7 +83,12 @@ test('Value is initialized with the embeddables title', async () => {
test('Calls updateInput with a new title', async () => {
const updateInput = jest.spyOn(embeddable, 'updateInput');
const component = mountWithIntl(
<CustomizePanelEditor embeddable={embeddable} timeRangeCompatible={true} onClose={() => {}} />
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={true}
onClose={() => {}}
onEdit={() => {}}
/>
);
const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input');
@ -86,7 +105,12 @@ test('Calls updateInput with a new title', async () => {
test('Input value shows custom title if one given', async () => {
embeddable.updateInput({ title: 'new title' });
const component = mountWithIntl(
<CustomizePanelEditor embeddable={embeddable} timeRangeCompatible={true} onClose={() => {}} />
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={true}
onClose={() => {}}
onEdit={() => {}}
/>
);
const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input');
@ -98,7 +122,12 @@ test('Input value shows custom title if one given', async () => {
test('Reset updates the input values with the default properties when the embeddable has overridden the properties', async () => {
embeddable.updateInput({ title: 'my custom title', description: 'my custom description' });
const component = mountWithIntl(
<CustomizePanelEditor embeddable={embeddable} timeRangeCompatible={true} onClose={() => {}} />
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={true}
onClose={() => {}}
onEdit={() => {}}
/>
);
const titleField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input');
@ -118,7 +147,12 @@ test('Reset updates the input values with the default properties when the embedd
test('Reset updates the input with the default properties when the embeddable has no property overrides', async () => {
const component = mountWithIntl(
<CustomizePanelEditor embeddable={embeddable} timeRangeCompatible={true} onClose={() => {}} />
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={true}
onClose={() => {}}
onEdit={() => {}}
/>
);
const titleField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input');
@ -142,7 +176,12 @@ test('Reset updates the input with the default properties when the embeddable ha
test('Reset title calls updateInput with undefined', async () => {
const updateInput = jest.spyOn(embeddable, 'updateInput');
const component = mountWithIntl(
<CustomizePanelEditor embeddable={embeddable} timeRangeCompatible={true} onClose={() => {}} />
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={true}
onClose={() => {}}
onEdit={() => {}}
/>
);
const inputField = findTestSubject(component, 'customEmbeddablePanelTitleInput').find('input');
@ -160,7 +199,12 @@ test('Reset title calls updateInput with undefined', async () => {
test('Reset description calls updateInput with undefined', async () => {
const updateInput = jest.spyOn(embeddable, 'updateInput');
const component = mountWithIntl(
<CustomizePanelEditor embeddable={embeddable} timeRangeCompatible={true} onClose={() => {}} />
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={true}
onClose={() => {}}
onEdit={() => {}}
/>
);
const inputField = findTestSubject(component, 'customEmbeddablePanelDescriptionInput').find(
@ -180,7 +224,12 @@ test('Reset description calls updateInput with undefined', async () => {
test('Can set title and description to an empty string', async () => {
const updateInput = jest.spyOn(embeddable, 'updateInput');
const component = mountWithIntl(
<CustomizePanelEditor embeddable={embeddable} timeRangeCompatible={true} onClose={() => {}} />
<CustomizePanelEditor
embeddable={embeddable}
timeRangeCompatible={true}
onClose={() => {}}
onEdit={() => {}}
/>
);
for (const subject of [

View file

@ -30,7 +30,10 @@
"@kbn/usage-collection-plugin",
"@kbn/ui-theme",
"@kbn/core-mount-utils-browser",
"@kbn/content-management-plugin"
"@kbn/content-management-plugin",
"@kbn/react-kibana-mount",
"@kbn/unified-search-plugin",
"@kbn/data-views-plugin",
],
"exclude": ["target/**/*"]
}

View file

@ -8,7 +8,6 @@
import { i18n } from '@kbn/i18n';
import { ThemeServiceSetup } from '@kbn/core/public';
import type { IEmbeddable } from '@kbn/embeddable-plugin/public';
import { toMountPoint } from '@kbn/kibana-react-plugin/public';
import { IncompatibleActionError, UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public';
// for cleanup esFilters need to fix the issue https://github.com/elastic/kibana/issues/131292
@ -22,7 +21,11 @@ export const ACTION_GLOBAL_APPLY_FILTER = 'ACTION_GLOBAL_APPLY_FILTER';
export interface ApplyGlobalFilterActionContext {
filters: Filter[];
timeFieldName?: string;
embeddable?: IEmbeddable;
// Need to make this unknown to prevent circular dependencies.
// Apps using this property will need to cast to `IEmbeddable`.
// TODO: We should consider moving these commonly used types into a separate package to avoid circular dependencies
// https://github.com/elastic/kibana/issues/163994
embeddable?: unknown;
// controlledBy is an optional key in filter.meta that identifies the owner of a filter
// Pass controlledBy to cleanup an existing filter(s) owned by embeddable prior to adding new filters
controlledBy?: string;

View file

@ -22,6 +22,7 @@ export interface FilterBadgeProps {
valueLabel: string;
hideAlias?: boolean;
filterLabelStatus: FilterLabelStatus;
readOnly?: boolean;
}
function FilterBadge({
@ -30,6 +31,7 @@ function FilterBadge({
valueLabel,
hideAlias,
filterLabelStatus,
readOnly,
...rest
}: FilterBadgeProps) {
const { euiTheme } = useEuiTheme();
@ -53,7 +55,7 @@ function FilterBadge({
<EuiBadge
className={badgePaddingCss(euiTheme)}
color="hollow"
iconType="cross"
iconType={readOnly ? 'cross' : undefined}
iconSide="right"
{...rest}
>

View file

@ -15,7 +15,6 @@
"@kbn/data-plugin",
"@kbn/data-views-plugin",
"@kbn/data-view-editor-plugin",
"@kbn/embeddable-plugin",
"@kbn/usage-collection-plugin",
"@kbn/kibana-utils-plugin",
"@kbn/kibana-react-plugin",
@ -40,7 +39,7 @@
"@kbn/saved-objects-management-plugin",
"@kbn/text-based-languages",
"@kbn/text-based-editor",
"@kbn/core-doc-links-browser"
"@kbn/core-doc-links-browser",
],
"exclude": [
"target/**/*",

View file

@ -41,7 +41,7 @@ interface UrlDrilldownDeps {
application: () => ApplicationStart;
}
export type ActionContext = ApplyGlobalFilterActionContext;
export type ActionContext = ApplyGlobalFilterActionContext & { embeddable: IEmbeddable };
export interface Config extends SerializableRecord {
openInNewTab: boolean;