Lens drilldowns (#65675)
This commit is contained in:
parent
f7a89601c7
commit
52070091f4
|
@ -53,16 +53,16 @@ export function selectRangeAction(
|
|||
});
|
||||
},
|
||||
isCompatible,
|
||||
execute: async ({ timeFieldName, data }: SelectRangeActionContext) => {
|
||||
if (!(await isCompatible({ timeFieldName, data }))) {
|
||||
execute: async ({ data }: SelectRangeActionContext) => {
|
||||
if (!(await isCompatible({ data }))) {
|
||||
throw new IncompatibleActionError();
|
||||
}
|
||||
|
||||
const selectedFilters = await createFiltersFromRangeSelectAction(data);
|
||||
|
||||
if (timeFieldName) {
|
||||
if (data.timeFieldName) {
|
||||
const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
|
||||
timeFieldName,
|
||||
data.timeFieldName,
|
||||
selectedFilters
|
||||
);
|
||||
filterManager.addFilters(restOfFilters);
|
||||
|
|
|
@ -57,12 +57,12 @@ export function valueClickAction(
|
|||
});
|
||||
},
|
||||
isCompatible,
|
||||
execute: async (context: ValueClickActionContext) => {
|
||||
if (!(await isCompatible(context))) {
|
||||
execute: async ({ data }: ValueClickActionContext) => {
|
||||
if (!(await isCompatible({ data }))) {
|
||||
throw new IncompatibleActionError();
|
||||
}
|
||||
|
||||
const filters: Filter[] = await createFiltersFromValueClickAction(context.data);
|
||||
const filters: Filter[] = await createFiltersFromValueClickAction(data);
|
||||
|
||||
let selectedFilters = filters;
|
||||
|
||||
|
@ -98,9 +98,9 @@ export function valueClickAction(
|
|||
selectedFilters = await filterSelectionPromise;
|
||||
}
|
||||
|
||||
if (context.timeFieldName) {
|
||||
if (data.timeFieldName) {
|
||||
const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
|
||||
context.timeFieldName,
|
||||
data.timeFieldName,
|
||||
selectedFilters
|
||||
);
|
||||
filterManager.addFilters(restOfFilters);
|
||||
|
|
|
@ -27,7 +27,6 @@ export interface EmbeddableContext {
|
|||
|
||||
export interface ValueClickTriggerContext<T extends IEmbeddable = IEmbeddable> {
|
||||
embeddable?: T;
|
||||
timeFieldName?: string;
|
||||
data: {
|
||||
data: Array<{
|
||||
table: Pick<KibanaDatatable, 'rows' | 'columns'>;
|
||||
|
@ -35,6 +34,7 @@ export interface ValueClickTriggerContext<T extends IEmbeddable = IEmbeddable> {
|
|||
row: number;
|
||||
value: any;
|
||||
}>;
|
||||
timeFieldName?: string;
|
||||
negate?: boolean;
|
||||
};
|
||||
}
|
||||
|
@ -45,11 +45,11 @@ export const isValueClickTriggerContext = (
|
|||
|
||||
export interface RangeSelectTriggerContext<T extends IEmbeddable = IEmbeddable> {
|
||||
embeddable?: T;
|
||||
timeFieldName?: string;
|
||||
data: {
|
||||
table: KibanaDatatable;
|
||||
column: number;
|
||||
range: number[];
|
||||
timeFieldName?: string;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -264,8 +264,7 @@ export class VisualizeEmbeddable extends Embeddable<VisualizeInput, VisualizeOut
|
|||
event.name === 'brush' ? VIS_EVENT_TO_TRIGGER.brush : VIS_EVENT_TO_TRIGGER.filter;
|
||||
const context = {
|
||||
embeddable: this,
|
||||
timeFieldName: this.vis.data.indexPattern!.timeFieldName!,
|
||||
data: event.data,
|
||||
data: { timeFieldName: this.vis.data.indexPattern?.timeFieldName!, ...event.data },
|
||||
};
|
||||
|
||||
getUiActions()
|
||||
|
|
|
@ -27,9 +27,6 @@ import { getDocumentationLinks } from './lib/documentation_links';
|
|||
import { HelpMenu } from './components/help_menu/help_menu';
|
||||
import { createStore, destroyStore } from './store';
|
||||
|
||||
import { VALUE_CLICK_TRIGGER, ActionByType } from '../../../../src/plugins/ui_actions/public';
|
||||
/* eslint-disable */
|
||||
import { ACTION_VALUE_CLICK } from '../../../../src/plugins/data/public/actions/value_click_action';
|
||||
/* eslint-enable */
|
||||
import { init as initStatsReporter } from './lib/ui_metric';
|
||||
|
||||
|
@ -45,16 +42,6 @@ import './style/index.scss';
|
|||
|
||||
const { ReadOnlyBadge: strings } = CapabilitiesStrings;
|
||||
|
||||
let restoreAction: ActionByType<any> | undefined;
|
||||
const emptyAction = {
|
||||
id: 'empty-action',
|
||||
type: '',
|
||||
getDisplayName: () => 'empty action',
|
||||
getIconType: () => undefined,
|
||||
isCompatible: async () => true,
|
||||
execute: async () => undefined,
|
||||
} as ActionByType<any>;
|
||||
|
||||
export const renderApp = (
|
||||
coreStart: CoreStart,
|
||||
plugins: CanvasStartDeps,
|
||||
|
@ -134,17 +121,6 @@ export const initializeCanvas = async (
|
|||
},
|
||||
});
|
||||
|
||||
// TODO: We need this to disable the filtering modal from popping up in lens embeds until
|
||||
// they honor the disableTriggers parameter
|
||||
const action = startPlugins.uiActions.getAction(ACTION_VALUE_CLICK);
|
||||
|
||||
if (action) {
|
||||
restoreAction = action;
|
||||
|
||||
startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, action.id);
|
||||
startPlugins.uiActions.addTriggerAction(VALUE_CLICK_TRIGGER, emptyAction);
|
||||
}
|
||||
|
||||
if (setupPlugins.usageCollection) {
|
||||
initStatsReporter(setupPlugins.usageCollection.reportUiStats);
|
||||
}
|
||||
|
@ -158,12 +134,6 @@ export const teardownCanvas = (coreStart: CoreStart, startPlugins: CanvasStartDe
|
|||
resetInterpreter();
|
||||
destroyStore();
|
||||
|
||||
startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, emptyAction.id);
|
||||
if (restoreAction) {
|
||||
startPlugins.uiActions.addTriggerAction(VALUE_CLICK_TRIGGER, restoreAction);
|
||||
restoreAction = undefined;
|
||||
}
|
||||
|
||||
coreStart.chrome.setBadge(undefined);
|
||||
coreStart.chrome.setHelpExtension(undefined);
|
||||
|
||||
|
|
|
@ -42,14 +42,6 @@ export class FlyoutCreateDrilldownAction implements ActionByType<typeof OPEN_FLY
|
|||
if (!supportedTriggers || !supportedTriggers.length) return false;
|
||||
if (context.embeddable.getRoot().type !== 'dashboard') return false;
|
||||
|
||||
/**
|
||||
* Temporarily disable drilldowns for Lens as Lens embeddable does not have
|
||||
* `.embeddable` field on VALUE_CLICK_TRIGGER context.
|
||||
*
|
||||
* @todo Remove this condition once Lens adds `.embeddable` to field to context.
|
||||
*/
|
||||
if (context.embeddable.type === 'lens') return false;
|
||||
|
||||
return supportedTriggers.indexOf('VALUE_CLICK_TRIGGER') > -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -134,10 +134,12 @@ describe('.execute() & getHref', () => {
|
|||
};
|
||||
|
||||
const context = ({
|
||||
data: useRangeEvent
|
||||
? ({ range: {} } as RangeSelectTriggerContext['data'])
|
||||
: ({ data: [] } as ValueClickTriggerContext['data']),
|
||||
timeFieldName: 'order_date',
|
||||
data: {
|
||||
...(useRangeEvent
|
||||
? ({ range: {} } as RangeSelectTriggerContext['data'])
|
||||
: ({ data: [] } as ValueClickTriggerContext['data'])),
|
||||
timeFieldName: 'order_date',
|
||||
},
|
||||
embeddable: {
|
||||
getInput: () => ({
|
||||
filters: [],
|
||||
|
|
|
@ -127,9 +127,9 @@ export class DashboardToDashboardDrilldown
|
|||
}
|
||||
})();
|
||||
|
||||
if (context.timeFieldName) {
|
||||
if (context.data.timeFieldName) {
|
||||
const { timeRangeFilter, restOfFilters } = esFilters.extractTimeFilter(
|
||||
context.timeFieldName,
|
||||
context.data.timeFieldName,
|
||||
filtersFromEvent
|
||||
);
|
||||
filtersFromEvent = restOfFilters;
|
||||
|
|
|
@ -9,10 +9,9 @@
|
|||
"expressions",
|
||||
"navigation",
|
||||
"kibanaLegacy",
|
||||
"uiActions",
|
||||
"visualizations",
|
||||
"dashboard"
|
||||
],
|
||||
"optionalPlugins": ["embeddable", "usageCollection", "taskManager"],
|
||||
"optionalPlugins": ["embeddable", "usageCollection", "taskManager", "uiActions"],
|
||||
"configPath": ["xpack", "lens"]
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import { DatatableProps } from './expression';
|
|||
import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks';
|
||||
import { IFieldFormat } from '../../../../../src/plugins/data/public';
|
||||
import { IAggType } from 'src/plugins/data/public';
|
||||
const executeTriggerActions = jest.fn();
|
||||
const onClickValue = jest.fn();
|
||||
|
||||
function sampleArgs() {
|
||||
const data: LensMultiTable = {
|
||||
|
@ -66,7 +66,7 @@ describe('datatable_expression', () => {
|
|||
data={data}
|
||||
args={args}
|
||||
formatFactory={x => x as IFieldFormat}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
getType={jest.fn()}
|
||||
/>
|
||||
)
|
||||
|
@ -87,7 +87,7 @@ describe('datatable_expression', () => {
|
|||
}}
|
||||
args={args}
|
||||
formatFactory={x => x as IFieldFormat}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
getType={jest.fn(() => ({ type: 'buckets' } as IAggType))}
|
||||
/>
|
||||
);
|
||||
|
@ -97,18 +97,16 @@ describe('datatable_expression', () => {
|
|||
.first()
|
||||
.simulate('click');
|
||||
|
||||
expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', {
|
||||
data: {
|
||||
data: [
|
||||
{
|
||||
column: 0,
|
||||
row: 0,
|
||||
table: data.tables.l1,
|
||||
value: 10110,
|
||||
},
|
||||
],
|
||||
negate: true,
|
||||
},
|
||||
expect(onClickValue).toHaveBeenCalledWith({
|
||||
data: [
|
||||
{
|
||||
column: 0,
|
||||
row: 0,
|
||||
table: data.tables.l1,
|
||||
value: 10110,
|
||||
},
|
||||
],
|
||||
negate: true,
|
||||
timeFieldName: undefined,
|
||||
});
|
||||
});
|
||||
|
@ -127,7 +125,7 @@ describe('datatable_expression', () => {
|
|||
}}
|
||||
args={args}
|
||||
formatFactory={x => x as IFieldFormat}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
getType={jest.fn(() => ({ type: 'buckets' } as IAggType))}
|
||||
/>
|
||||
);
|
||||
|
@ -137,18 +135,16 @@ describe('datatable_expression', () => {
|
|||
.at(3)
|
||||
.simulate('click');
|
||||
|
||||
expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', {
|
||||
data: {
|
||||
data: [
|
||||
{
|
||||
column: 1,
|
||||
row: 0,
|
||||
table: data.tables.l1,
|
||||
value: 1588024800000,
|
||||
},
|
||||
],
|
||||
negate: false,
|
||||
},
|
||||
expect(onClickValue).toHaveBeenCalledWith({
|
||||
data: [
|
||||
{
|
||||
column: 1,
|
||||
row: 0,
|
||||
table: data.tables.l1,
|
||||
value: 1588024800000,
|
||||
},
|
||||
],
|
||||
negate: false,
|
||||
timeFieldName: 'b',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,17 +10,17 @@ import { i18n } from '@kbn/i18n';
|
|||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiFlexItem, EuiToolTip } from '@elastic/eui';
|
||||
import { IAggType } from 'src/plugins/data/public';
|
||||
import { FormatFactory, LensMultiTable } from '../types';
|
||||
import {
|
||||
FormatFactory,
|
||||
ILensInterpreterRenderHandlers,
|
||||
LensFilterEvent,
|
||||
LensMultiTable,
|
||||
} from '../types';
|
||||
import {
|
||||
ExpressionFunctionDefinition,
|
||||
ExpressionRenderDefinition,
|
||||
IInterpreterRenderHandlers,
|
||||
} from '../../../../../src/plugins/expressions/public';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
import { getExecuteTriggerActions } from '../services';
|
||||
export interface DatatableColumns {
|
||||
columnIds: string[];
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ export interface DatatableProps {
|
|||
|
||||
type DatatableRenderProps = DatatableProps & {
|
||||
formatFactory: FormatFactory;
|
||||
executeTriggerActions: UiActionsStart['executeTriggerActions'];
|
||||
onClickValue: (data: LensFilterEvent['data']) => void;
|
||||
getType: (name: string) => IAggType;
|
||||
};
|
||||
|
||||
|
@ -125,17 +125,19 @@ export const getDatatableRenderer = (dependencies: {
|
|||
render: async (
|
||||
domNode: Element,
|
||||
config: DatatableProps,
|
||||
handlers: IInterpreterRenderHandlers
|
||||
handlers: ILensInterpreterRenderHandlers
|
||||
) => {
|
||||
const resolvedFormatFactory = await dependencies.formatFactory;
|
||||
const executeTriggerActions = getExecuteTriggerActions();
|
||||
const resolvedGetType = await dependencies.getType;
|
||||
const onClickValue = (data: LensFilterEvent['data']) => {
|
||||
handlers.event({ name: 'filter', data });
|
||||
};
|
||||
ReactDOM.render(
|
||||
<I18nProvider>
|
||||
<DatatableComponent
|
||||
{...config}
|
||||
formatFactory={resolvedFormatFactory}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
getType={resolvedGetType}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
|
@ -162,21 +164,19 @@ export function DatatableComponent(props: DatatableRenderProps) {
|
|||
const timeFieldName = negate && isDateHistogram ? undefined : col?.meta?.aggConfigParams?.field;
|
||||
const rowIndex = firstTable.rows.findIndex(row => row[field] === value);
|
||||
|
||||
const context: ValueClickTriggerContext = {
|
||||
data: {
|
||||
negate,
|
||||
data: [
|
||||
{
|
||||
row: rowIndex,
|
||||
column: colIndex,
|
||||
value,
|
||||
table: firstTable,
|
||||
},
|
||||
],
|
||||
},
|
||||
const data: LensFilterEvent['data'] = {
|
||||
negate,
|
||||
data: [
|
||||
{
|
||||
row: rowIndex,
|
||||
column: colIndex,
|
||||
value,
|
||||
table: firstTable,
|
||||
},
|
||||
],
|
||||
timeFieldName,
|
||||
};
|
||||
props.executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context);
|
||||
props.onClickValue(data);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -4,12 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { CoreSetup, CoreStart } from 'kibana/public';
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { datatableVisualization } from './visualization';
|
||||
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import { datatable, datatableColumns, getDatatableRenderer } from './expression';
|
||||
import { EditorFrameSetup, FormatFactory } from '../types';
|
||||
import { setExecuteTriggerActions } from '../services';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
|
||||
|
||||
|
@ -42,7 +41,4 @@ export class DatatableVisualization {
|
|||
);
|
||||
editorFrame.registerVisualization(datatableVisualization);
|
||||
}
|
||||
start(core: CoreStart, { uiActions }: DatatableVisualizationPluginStartPlugins) {
|
||||
setExecuteTriggerActions(uiActions.executeTriggerActions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ import {
|
|||
import { ReactExpressionRendererType } from 'src/plugins/expressions/public';
|
||||
import { DragDrop } from '../../drag_drop';
|
||||
import { FrameLayout } from './frame_layout';
|
||||
import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks';
|
||||
import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks';
|
||||
|
||||
function generateSuggestion(state = {}): DatasourceSuggestion {
|
||||
return {
|
||||
|
@ -48,6 +51,11 @@ function getDefaultProps() {
|
|||
query: { query: '', language: 'lucene' },
|
||||
filters: [],
|
||||
core: coreMock.createSetup(),
|
||||
plugins: {
|
||||
uiActions: uiActionsPluginMock.createStartContract(),
|
||||
data: dataPluginMock.createStartContract(),
|
||||
expressions: expressionsPluginMock.createStartContract(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import { getSavedObjectFormat } from './save';
|
|||
import { WorkspacePanelWrapper } from './workspace_panel_wrapper';
|
||||
import { generateId } from '../../id_generator';
|
||||
import { Filter, Query, SavedQuery } from '../../../../../../src/plugins/data/public';
|
||||
import { EditorFrameStartPlugins } from '../service';
|
||||
|
||||
export interface EditorFrameProps {
|
||||
doc?: Document;
|
||||
|
@ -36,6 +37,7 @@ export interface EditorFrameProps {
|
|||
ExpressionRenderer: ReactExpressionRendererType;
|
||||
onError: (e: { message: string }) => void;
|
||||
core: CoreSetup | CoreStart;
|
||||
plugins: EditorFrameStartPlugins;
|
||||
dateRange: {
|
||||
fromDate: string;
|
||||
toDate: string;
|
||||
|
@ -285,6 +287,7 @@ export function EditorFrame(props: EditorFrameProps) {
|
|||
dispatch={dispatch}
|
||||
ExpressionRenderer={props.ExpressionRenderer}
|
||||
core={props.core}
|
||||
plugins={props.plugins}
|
||||
/>
|
||||
</WorkspacePanelWrapper>
|
||||
)
|
||||
|
|
|
@ -9,6 +9,9 @@ import { EditorFrameProps } from './index';
|
|||
import { Datasource, Visualization } from '../../types';
|
||||
import { createExpressionRendererMock } from '../mocks';
|
||||
import { coreMock } from 'src/core/public/mocks';
|
||||
import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks';
|
||||
import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks';
|
||||
|
||||
describe('editor_frame state management', () => {
|
||||
describe('initialization', () => {
|
||||
|
@ -24,6 +27,11 @@ describe('editor_frame state management', () => {
|
|||
ExpressionRenderer: createExpressionRendererMock(),
|
||||
onChange: jest.fn(),
|
||||
core: coreMock.createSetup(),
|
||||
plugins: {
|
||||
uiActions: uiActionsPluginMock.createStartContract(),
|
||||
data: dataPluginMock.createStartContract(),
|
||||
expressions: expressionsPluginMock.createStartContract(),
|
||||
},
|
||||
dateRange: { fromDate: 'now-7d', toDate: 'now' },
|
||||
query: { query: '', language: 'lucene' },
|
||||
filters: [],
|
||||
|
|
|
@ -22,6 +22,10 @@ import { DragDrop, ChildDragDropProvider } from '../../drag_drop';
|
|||
import { Ast } from '@kbn/interpreter/common';
|
||||
import { coreMock } from 'src/core/public/mocks';
|
||||
import { esFilters, IFieldType, IIndexPattern } from '../../../../../../src/plugins/data/public';
|
||||
import { TriggerId, UiActionsStart } from '../../../../../../src/plugins/ui_actions/public';
|
||||
import { uiActionsPluginMock } from '../../../../../../src/plugins/ui_actions/public/mocks';
|
||||
import { TriggerContract } from '../../../../../../src/plugins/ui_actions/public/triggers';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public/embeddable';
|
||||
|
||||
describe('workspace_panel', () => {
|
||||
let mockVisualization: jest.Mocked<Visualization>;
|
||||
|
@ -29,10 +33,15 @@ describe('workspace_panel', () => {
|
|||
let mockDatasource: DatasourceMock;
|
||||
|
||||
let expressionRendererMock: jest.Mock<React.ReactElement, [ReactExpressionRendererProps]>;
|
||||
let uiActionsMock: jest.Mocked<UiActionsStart>;
|
||||
let trigger: jest.Mocked<TriggerContract<TriggerId>>;
|
||||
|
||||
let instance: ReactWrapper<WorkspacePanelProps>;
|
||||
|
||||
beforeEach(() => {
|
||||
trigger = ({ exec: jest.fn() } as unknown) as jest.Mocked<TriggerContract<TriggerId>>;
|
||||
uiActionsMock = uiActionsPluginMock.createStartContract();
|
||||
uiActionsMock.getTrigger.mockReturnValue(trigger);
|
||||
mockVisualization = createMockVisualization();
|
||||
mockVisualization2 = createMockVisualization();
|
||||
|
||||
|
@ -60,6 +69,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -82,6 +92,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -104,6 +115,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -140,6 +152,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -198,6 +211,48 @@ describe('workspace_panel', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
it('should execute a trigger on expression event', () => {
|
||||
const framePublicAPI = createMockFramePublicAPI();
|
||||
framePublicAPI.datasourceLayers = {
|
||||
first: mockDatasource.publicAPIMock,
|
||||
};
|
||||
mockDatasource.toExpression.mockReturnValue('datasource');
|
||||
mockDatasource.getLayers.mockReturnValue(['first']);
|
||||
|
||||
instance = mount(
|
||||
<InnerWorkspacePanel
|
||||
activeDatasourceId={'mock'}
|
||||
datasourceStates={{
|
||||
mock: {
|
||||
state: {},
|
||||
isLoading: false,
|
||||
},
|
||||
}}
|
||||
datasourceMap={{
|
||||
mock: mockDatasource,
|
||||
}}
|
||||
framePublicAPI={framePublicAPI}
|
||||
activeVisualizationId="vis"
|
||||
visualizationMap={{
|
||||
vis: { ...mockVisualization, toExpression: () => 'vis' },
|
||||
}}
|
||||
visualizationState={{}}
|
||||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
|
||||
const onEvent = expressionRendererMock.mock.calls[0][0].onEvent!;
|
||||
|
||||
const eventData = {};
|
||||
onEvent({ name: 'brush', data: eventData });
|
||||
|
||||
expect(uiActionsMock.getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.brush);
|
||||
expect(trigger.exec).toHaveBeenCalledWith({ data: eventData });
|
||||
});
|
||||
|
||||
it('should include data fetching for each layer in the expression', () => {
|
||||
const mockDatasource2 = createMockDatasource('a');
|
||||
const framePublicAPI = createMockFramePublicAPI();
|
||||
|
@ -237,6 +292,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -316,6 +372,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -370,6 +427,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -424,6 +482,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -461,6 +520,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -504,6 +564,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={() => {}}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -559,6 +620,7 @@ describe('workspace_panel', () => {
|
|||
dispatch={mockDispatch}
|
||||
ExpressionRenderer={expressionRendererMock}
|
||||
core={coreMock.createSetup()}
|
||||
plugins={{ uiActions: uiActionsMock }}
|
||||
/>
|
||||
</ChildDragDropProvider>
|
||||
);
|
||||
|
|
|
@ -17,14 +17,25 @@ import {
|
|||
EuiButtonEmpty,
|
||||
} from '@elastic/eui';
|
||||
import { CoreStart, CoreSetup } from 'kibana/public';
|
||||
import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public';
|
||||
import {
|
||||
ExpressionRendererEvent,
|
||||
ReactExpressionRendererType,
|
||||
} from '../../../../../../src/plugins/expressions/public';
|
||||
import { Action } from './state_management';
|
||||
import { Datasource, Visualization, FramePublicAPI } from '../../types';
|
||||
import {
|
||||
Datasource,
|
||||
Visualization,
|
||||
FramePublicAPI,
|
||||
isLensBrushEvent,
|
||||
isLensFilterEvent,
|
||||
} from '../../types';
|
||||
import { DragDrop, DragContext } from '../../drag_drop';
|
||||
import { getSuggestions, switchToSuggestion } from './suggestion_helpers';
|
||||
import { buildExpression } from './expression_helpers';
|
||||
import { debouncedComponent } from '../../debounced_component';
|
||||
import { trackUiEvent } from '../../lens_ui_telemetry';
|
||||
import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public';
|
||||
|
||||
export interface WorkspacePanelProps {
|
||||
activeVisualizationId: string | null;
|
||||
|
@ -43,6 +54,7 @@ export interface WorkspacePanelProps {
|
|||
dispatch: (action: Action) => void;
|
||||
ExpressionRenderer: ReactExpressionRendererType;
|
||||
core: CoreStart | CoreSetup;
|
||||
plugins: { uiActions?: UiActionsStart };
|
||||
}
|
||||
|
||||
export const WorkspacePanel = debouncedComponent(InnerWorkspacePanel);
|
||||
|
@ -58,6 +70,7 @@ export function InnerWorkspacePanel({
|
|||
framePublicAPI,
|
||||
dispatch,
|
||||
core,
|
||||
plugins,
|
||||
ExpressionRenderer: ExpressionRendererComponent,
|
||||
}: WorkspacePanelProps) {
|
||||
const IS_DARK_THEME = core.uiSettings.get('theme:darkMode');
|
||||
|
@ -211,6 +224,22 @@ export function InnerWorkspacePanel({
|
|||
className="lnsExpressionRenderer__component"
|
||||
padding="m"
|
||||
expression={expression!}
|
||||
onEvent={(event: ExpressionRendererEvent) => {
|
||||
if (!plugins.uiActions) {
|
||||
// ui actions not available, not handling event...
|
||||
return;
|
||||
}
|
||||
if (isLensBrushEvent(event)) {
|
||||
plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({
|
||||
data: event.data,
|
||||
});
|
||||
}
|
||||
if (isLensFilterEvent(event)) {
|
||||
plugins.uiActions.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({
|
||||
data: event.data,
|
||||
});
|
||||
}
|
||||
}}
|
||||
renderError={(errorMessage?: string | null) => {
|
||||
return (
|
||||
<EuiFlexGroup direction="column" alignItems="center">
|
||||
|
|
|
@ -10,6 +10,7 @@ import { ReactExpressionRendererProps } from 'src/plugins/expressions/public';
|
|||
import { Query, TimeRange, Filter, TimefilterContract } from 'src/plugins/data/public';
|
||||
import { Document } from '../../persistence';
|
||||
import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public/embeddable';
|
||||
|
||||
jest.mock('../../../../../../src/plugins/inspector/public/', () => ({
|
||||
isAvailable: false,
|
||||
|
@ -34,10 +35,14 @@ const savedVis: Document = {
|
|||
describe('embeddable', () => {
|
||||
let mountpoint: HTMLDivElement;
|
||||
let expressionRenderer: jest.Mock<null, [ReactExpressionRendererProps]>;
|
||||
let getTrigger: jest.Mock;
|
||||
let trigger: { exec: jest.Mock };
|
||||
|
||||
beforeEach(() => {
|
||||
mountpoint = document.createElement('div');
|
||||
expressionRenderer = jest.fn(_props => null);
|
||||
trigger = { exec: jest.fn() };
|
||||
getTrigger = jest.fn(() => trigger);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -48,6 +53,7 @@ describe('embeddable', () => {
|
|||
const embeddable = new Embeddable(
|
||||
dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
expressionRenderer,
|
||||
getTrigger,
|
||||
{
|
||||
editPath: '',
|
||||
editUrl: '',
|
||||
|
@ -70,6 +76,7 @@ describe('embeddable', () => {
|
|||
const embeddable = new Embeddable(
|
||||
dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
expressionRenderer,
|
||||
getTrigger,
|
||||
{
|
||||
editPath: '',
|
||||
editUrl: '',
|
||||
|
@ -97,6 +104,7 @@ describe('embeddable', () => {
|
|||
const embeddable = new Embeddable(
|
||||
dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
expressionRenderer,
|
||||
getTrigger,
|
||||
{
|
||||
editPath: '',
|
||||
editUrl: '',
|
||||
|
@ -114,6 +122,32 @@ describe('embeddable', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should execute trigger on event from expression renderer', () => {
|
||||
const embeddable = new Embeddable(
|
||||
dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
expressionRenderer,
|
||||
getTrigger,
|
||||
{
|
||||
editPath: '',
|
||||
editUrl: '',
|
||||
editable: true,
|
||||
savedVis,
|
||||
},
|
||||
{ id: '123' }
|
||||
);
|
||||
embeddable.render(mountpoint);
|
||||
|
||||
const onEvent = expressionRenderer.mock.calls[0][0].onEvent!;
|
||||
|
||||
const eventData = {};
|
||||
onEvent({ name: 'brush', data: eventData });
|
||||
|
||||
expect(getTrigger).toHaveBeenCalledWith(VIS_EVENT_TO_TRIGGER.brush);
|
||||
expect(trigger.exec).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ data: eventData, embeddable: expect.anything() })
|
||||
);
|
||||
});
|
||||
|
||||
it('should not re-render if only change is in disabled filter', () => {
|
||||
const timeRange: TimeRange = { from: 'now-15d', to: 'now' };
|
||||
const query: Query = { language: 'kquery', query: '' };
|
||||
|
@ -122,6 +156,7 @@ describe('embeddable', () => {
|
|||
const embeddable = new Embeddable(
|
||||
dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
expressionRenderer,
|
||||
getTrigger,
|
||||
{
|
||||
editPath: '',
|
||||
editUrl: '',
|
||||
|
@ -154,6 +189,7 @@ describe('embeddable', () => {
|
|||
const embeddable = new Embeddable(
|
||||
timefilter,
|
||||
expressionRenderer,
|
||||
getTrigger,
|
||||
{
|
||||
editPath: '',
|
||||
editUrl: '',
|
||||
|
|
|
@ -8,25 +8,30 @@ import _ from 'lodash';
|
|||
import React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import {
|
||||
Query,
|
||||
TimeRange,
|
||||
Filter,
|
||||
IIndexPattern,
|
||||
Query,
|
||||
TimefilterContract,
|
||||
TimeRange,
|
||||
} from 'src/plugins/data/public';
|
||||
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ReactExpressionRendererType } from '../../../../../../src/plugins/expressions/public';
|
||||
import {
|
||||
ExpressionRendererEvent,
|
||||
ReactExpressionRendererType,
|
||||
} from '../../../../../../src/plugins/expressions/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizations/public';
|
||||
|
||||
import {
|
||||
Embeddable as AbstractEmbeddable,
|
||||
EmbeddableInput,
|
||||
EmbeddableOutput,
|
||||
IContainer,
|
||||
EmbeddableInput,
|
||||
} from '../../../../../../src/plugins/embeddable/public';
|
||||
import { Document, DOC_TYPE } from '../../persistence';
|
||||
import { DOC_TYPE, Document } from '../../persistence';
|
||||
import { ExpressionWrapper } from './expression_wrapper';
|
||||
import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public';
|
||||
import { isLensBrushEvent, isLensFilterEvent } from '../../types';
|
||||
|
||||
export interface LensEmbeddableConfiguration {
|
||||
savedVis: Document;
|
||||
|
@ -50,6 +55,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
|
|||
type = DOC_TYPE;
|
||||
|
||||
private expressionRenderer: ReactExpressionRendererType;
|
||||
private getTrigger: UiActionsStart['getTrigger'] | undefined;
|
||||
private savedVis: Document;
|
||||
private domNode: HTMLElement | Element | undefined;
|
||||
private subscription: Subscription;
|
||||
|
@ -65,6 +71,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
|
|||
constructor(
|
||||
timefilter: TimefilterContract,
|
||||
expressionRenderer: ReactExpressionRendererType,
|
||||
getTrigger: UiActionsStart['getTrigger'] | undefined,
|
||||
{ savedVis, editPath, editUrl, editable, indexPatterns }: LensEmbeddableConfiguration,
|
||||
initialInput: LensEmbeddableInput,
|
||||
parent?: IContainer
|
||||
|
@ -86,6 +93,7 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
|
|||
parent
|
||||
);
|
||||
|
||||
this.getTrigger = getTrigger;
|
||||
this.expressionRenderer = expressionRenderer;
|
||||
this.savedVis = savedVis;
|
||||
this.subscription = this.getInput$().subscribe(input => this.onContainerStateChanged(input));
|
||||
|
@ -100,6 +108,9 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
|
|||
switch (this.savedVis.visualizationType) {
|
||||
case 'lnsXY':
|
||||
return [VIS_EVENT_TO_TRIGGER.filter, VIS_EVENT_TO_TRIGGER.brush];
|
||||
case 'lnsDatatable':
|
||||
case 'lnsPie':
|
||||
return [VIS_EVENT_TO_TRIGGER.filter];
|
||||
case 'lnsMetric':
|
||||
default:
|
||||
return [];
|
||||
|
@ -140,11 +151,30 @@ export class Embeddable extends AbstractEmbeddable<LensEmbeddableInput, LensEmbe
|
|||
ExpressionRenderer={this.expressionRenderer}
|
||||
expression={this.savedVis.expression}
|
||||
context={this.currentContext}
|
||||
handleEvent={this.handleEvent}
|
||||
/>,
|
||||
domNode
|
||||
);
|
||||
}
|
||||
|
||||
handleEvent = (event: ExpressionRendererEvent) => {
|
||||
if (!this.getTrigger || this.input.disableTriggers) {
|
||||
return;
|
||||
}
|
||||
if (isLensBrushEvent(event)) {
|
||||
this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({
|
||||
data: event.data,
|
||||
embeddable: this,
|
||||
});
|
||||
}
|
||||
if (isLensFilterEvent(event)) {
|
||||
this.getTrigger(VIS_EVENT_TO_TRIGGER[event.name]).exec({
|
||||
data: event.data,
|
||||
embeddable: this,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
destroy() {
|
||||
super.destroy();
|
||||
if (this.domNode) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
import { Embeddable } from './embeddable';
|
||||
import { SavedObjectIndexStore, DOC_TYPE } from '../../persistence';
|
||||
import { getEditPath } from '../../../common';
|
||||
import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public';
|
||||
|
||||
interface StartServices {
|
||||
timefilter: TimefilterContract;
|
||||
|
@ -34,6 +35,7 @@ interface StartServices {
|
|||
savedObjectsClient: SavedObjectsClientContract;
|
||||
expressionRenderer: ReactExpressionRendererType;
|
||||
indexPatternService: IndexPatternsContract;
|
||||
uiActions?: UiActionsStart;
|
||||
}
|
||||
|
||||
export class EmbeddableFactory implements EmbeddableFactoryDefinition {
|
||||
|
@ -74,6 +76,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
indexPatternService,
|
||||
timefilter,
|
||||
expressionRenderer,
|
||||
uiActions,
|
||||
} = await this.getStartServices();
|
||||
const store = new SavedObjectIndexStore(savedObjectsClient);
|
||||
const savedVis = await store.load(savedObjectId);
|
||||
|
@ -99,6 +102,7 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition {
|
|||
return new Embeddable(
|
||||
timefilter,
|
||||
expressionRenderer,
|
||||
uiActions?.getTrigger,
|
||||
{
|
||||
savedVis,
|
||||
editPath: getEditPath(savedObjectId),
|
||||
|
|
|
@ -9,7 +9,10 @@ import { I18nProvider } from '@kbn/i18n/react';
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiText, EuiIcon } from '@elastic/eui';
|
||||
import { TimeRange, Filter, Query } from 'src/plugins/data/public';
|
||||
import { ReactExpressionRendererType } from 'src/plugins/expressions/public';
|
||||
import {
|
||||
ExpressionRendererEvent,
|
||||
ReactExpressionRendererType,
|
||||
} from 'src/plugins/expressions/public';
|
||||
|
||||
export interface ExpressionWrapperProps {
|
||||
ExpressionRenderer: ReactExpressionRendererType;
|
||||
|
@ -20,12 +23,14 @@ export interface ExpressionWrapperProps {
|
|||
filters?: Filter[];
|
||||
lastReloadRequestTime?: number;
|
||||
};
|
||||
handleEvent: (event: ExpressionRendererEvent) => void;
|
||||
}
|
||||
|
||||
export function ExpressionWrapper({
|
||||
ExpressionRenderer: ExpressionRendererComponent,
|
||||
expression,
|
||||
context,
|
||||
handleEvent,
|
||||
}: ExpressionWrapperProps) {
|
||||
return (
|
||||
<I18nProvider>
|
||||
|
@ -51,6 +56,7 @@ export function ExpressionWrapper({
|
|||
expression={expression}
|
||||
searchContext={{ ...context }}
|
||||
renderError={error => <div data-test-subj="expression-renderer-error">{error}</div>}
|
||||
onEvent={handleEvent}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -26,6 +26,7 @@ import { mergeTables } from './merge_tables';
|
|||
import { formatColumn } from './format_column';
|
||||
import { EmbeddableFactory } from './embeddable/embeddable_factory';
|
||||
import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
|
||||
export interface EditorFrameSetupPlugins {
|
||||
data: DataPublicPluginSetup;
|
||||
|
@ -37,6 +38,7 @@ export interface EditorFrameStartPlugins {
|
|||
data: DataPublicPluginStart;
|
||||
embeddable?: EmbeddableStart;
|
||||
expressions: ExpressionsStart;
|
||||
uiActions?: UiActionsStart;
|
||||
}
|
||||
|
||||
async function collectAsyncDefinitions<T extends { id: string }>(
|
||||
|
@ -73,6 +75,7 @@ export class EditorFrameService {
|
|||
timefilter: deps.data.query.timefilter.timefilter,
|
||||
expressionRenderer: deps.expressions.ReactExpressionRenderer,
|
||||
indexPatternService: deps.data.indexPatterns,
|
||||
uiActions: deps.uiActions,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -116,6 +119,7 @@ export class EditorFrameService {
|
|||
(doc && doc.visualizationType) || firstVisualizationId || null
|
||||
}
|
||||
core={core}
|
||||
plugins={plugins}
|
||||
ExpressionRenderer={plugins.expressions.ReactExpressionRenderer}
|
||||
doc={doc}
|
||||
dateRange={dateRange}
|
||||
|
|
|
@ -5,13 +5,12 @@
|
|||
*/
|
||||
|
||||
import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme';
|
||||
import { CoreSetup, CoreStart } from 'src/core/public';
|
||||
import { CoreSetup } from 'src/core/public';
|
||||
import { ExpressionsSetup } from 'src/plugins/expressions/public';
|
||||
import { pieVisualization } from './pie_visualization';
|
||||
import { pie, getPieRenderer } from './register_expression';
|
||||
import { EditorFrameSetup, FormatFactory } from '../types';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
import { setExecuteTriggerActions } from '../services';
|
||||
|
||||
export interface PieVisualizationPluginSetupPlugins {
|
||||
editorFrame: EditorFrameSetup;
|
||||
|
@ -44,10 +43,4 @@ export class PieVisualization {
|
|||
|
||||
editorFrame.registerVisualization(pieVisualization);
|
||||
}
|
||||
|
||||
start(core: CoreStart, { uiActions }: PieVisualizationPluginStartPlugins) {
|
||||
setExecuteTriggerActions(uiActions.executeTriggerActions);
|
||||
}
|
||||
|
||||
stop() {}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,8 @@ import {
|
|||
ExpressionRenderDefinition,
|
||||
ExpressionFunctionDefinition,
|
||||
} from 'src/plugins/expressions/public';
|
||||
import { LensMultiTable, FormatFactory } from '../types';
|
||||
import { LensMultiTable, FormatFactory, LensFilterEvent } from '../types';
|
||||
import { PieExpressionProps, PieExpressionArgs } from './types';
|
||||
import { getExecuteTriggerActions } from '../services';
|
||||
import { PieComponent } from './render_function';
|
||||
|
||||
export interface PieRender {
|
||||
|
@ -109,7 +108,9 @@ export const getPieRenderer = (dependencies: {
|
|||
config: PieExpressionProps,
|
||||
handlers: IInterpreterRenderHandlers
|
||||
) => {
|
||||
const executeTriggerActions = getExecuteTriggerActions();
|
||||
const onClickValue = (data: LensFilterEvent['data']) => {
|
||||
handlers.event({ name: 'filter', data });
|
||||
};
|
||||
const formatFactory = await dependencies.formatFactory;
|
||||
ReactDOM.render(
|
||||
<I18nProvider>
|
||||
|
@ -117,7 +118,7 @@ export const getPieRenderer = (dependencies: {
|
|||
{...config}
|
||||
{...dependencies}
|
||||
formatFactory={formatFactory}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
isDarkMode={dependencies.isDarkMode}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Settings } from '@elastic/charts';
|
||||
import { SeriesIdentifier, Settings } from '@elastic/charts';
|
||||
import { shallow } from 'enzyme';
|
||||
import { LensMultiTable } from '../types';
|
||||
import { PieComponent } from './render_function';
|
||||
|
@ -59,7 +59,7 @@ describe('PieVisualization component', () => {
|
|||
formatFactory: getFormatSpy,
|
||||
isDarkMode: false,
|
||||
chartTheme: {},
|
||||
executeTriggerActions: jest.fn(),
|
||||
onClickValue: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,58 @@ describe('PieVisualization component', () => {
|
|||
expect(component.find(Settings).prop('legendMaxDepth')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('it calls filter callback with the given context', () => {
|
||||
const defaultArgs = getDefaultArgs();
|
||||
const component = shallow(<PieComponent args={{ ...args }} {...defaultArgs} />);
|
||||
component
|
||||
.find(Settings)
|
||||
.first()
|
||||
.prop('onElementClick')!([[[{ groupByRollup: 6, value: 6 }], {} as SeriesIdentifier]]);
|
||||
|
||||
expect(defaultArgs.onClickValue.mock.calls[0][0]).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"data": Array [
|
||||
Object {
|
||||
"column": 0,
|
||||
"row": 0,
|
||||
"table": Object {
|
||||
"columns": Array [
|
||||
Object {
|
||||
"id": "a",
|
||||
"name": "a",
|
||||
},
|
||||
Object {
|
||||
"id": "b",
|
||||
"name": "b",
|
||||
},
|
||||
Object {
|
||||
"id": "c",
|
||||
"name": "c",
|
||||
},
|
||||
],
|
||||
"rows": Array [
|
||||
Object {
|
||||
"a": 6,
|
||||
"b": 2,
|
||||
"c": "I",
|
||||
"d": "Row 1",
|
||||
},
|
||||
Object {
|
||||
"a": 1,
|
||||
"b": 5,
|
||||
"c": "J",
|
||||
"d": "Row 2",
|
||||
},
|
||||
],
|
||||
"type": "kibana_datatable",
|
||||
},
|
||||
"value": 6,
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('it shows emptyPlaceholder for undefined grouped data', () => {
|
||||
const defaultData = getDefaultArgs().data;
|
||||
const emptyData: LensMultiTable = {
|
||||
|
|
|
@ -24,12 +24,10 @@ import {
|
|||
RecursivePartial,
|
||||
LayerValue,
|
||||
} from '@elastic/charts';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public';
|
||||
import { FormatFactory } from '../types';
|
||||
import { FormatFactory, LensFilterEvent } from '../types';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { CHART_NAMES, DEFAULT_PERCENT_DECIMALS } from './constants';
|
||||
import { ColumnGroups, PieExpressionProps } from './types';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
import { getSliceValueWithFallback, getFilterContext } from './render_helpers';
|
||||
import { EmptyPlaceholder } from '../shared_components';
|
||||
import './visualization.scss';
|
||||
|
@ -43,13 +41,13 @@ export function PieComponent(
|
|||
formatFactory: FormatFactory;
|
||||
chartTheme: Exclude<PartialTheme, undefined>;
|
||||
isDarkMode: boolean;
|
||||
executeTriggerActions: UiActionsStart['executeTriggerActions'];
|
||||
onClickValue: (data: LensFilterEvent['data']) => void;
|
||||
}
|
||||
) {
|
||||
const [firstTable] = Object.values(props.data.tables);
|
||||
const formatters: Record<string, ReturnType<FormatFactory>> = {};
|
||||
|
||||
const { chartTheme, isDarkMode, executeTriggerActions } = props;
|
||||
const { chartTheme, isDarkMode, onClickValue } = props;
|
||||
const {
|
||||
shape,
|
||||
groups,
|
||||
|
@ -246,7 +244,7 @@ export function PieComponent(
|
|||
firstTable
|
||||
);
|
||||
|
||||
executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context);
|
||||
onClickValue(context);
|
||||
}}
|
||||
/>
|
||||
<Partition
|
||||
|
|
|
@ -96,16 +96,14 @@ describe('render helpers', () => {
|
|||
],
|
||||
};
|
||||
expect(getFilterContext([{ groupByRollup: 'Test', value: 100 }], ['a'], table)).toEqual({
|
||||
data: {
|
||||
data: [
|
||||
{
|
||||
row: 1,
|
||||
column: 0,
|
||||
value: 'Test',
|
||||
table,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [
|
||||
{
|
||||
row: 1,
|
||||
column: 0,
|
||||
value: 'Test',
|
||||
table,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -124,16 +122,14 @@ describe('render helpers', () => {
|
|||
],
|
||||
};
|
||||
expect(getFilterContext([{ groupByRollup: 'Test', value: 100 }], ['a', 'b'], table)).toEqual({
|
||||
data: {
|
||||
data: [
|
||||
{
|
||||
row: 1,
|
||||
column: 0,
|
||||
value: 'Test',
|
||||
table,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [
|
||||
{
|
||||
row: 1,
|
||||
column: 0,
|
||||
value: 'Test',
|
||||
table,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -161,22 +157,20 @@ describe('render helpers', () => {
|
|||
table
|
||||
)
|
||||
).toEqual({
|
||||
data: {
|
||||
data: [
|
||||
{
|
||||
row: 1,
|
||||
column: 0,
|
||||
value: 'Test',
|
||||
table,
|
||||
},
|
||||
{
|
||||
row: 1,
|
||||
column: 1,
|
||||
value: 'Two',
|
||||
table,
|
||||
},
|
||||
],
|
||||
},
|
||||
data: [
|
||||
{
|
||||
row: 1,
|
||||
column: 0,
|
||||
value: 'Test',
|
||||
table,
|
||||
},
|
||||
{
|
||||
row: 1,
|
||||
column: 1,
|
||||
value: 'Two',
|
||||
table,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
import { Datum, LayerValue } from '@elastic/charts';
|
||||
import { KibanaDatatable, KibanaDatatableColumn } from 'src/plugins/expressions/public';
|
||||
import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public';
|
||||
import { ColumnGroups } from './types';
|
||||
import { LensFilterEvent } from '../types';
|
||||
|
||||
export function getSliceValueWithFallback(
|
||||
d: Datum,
|
||||
|
@ -28,7 +28,7 @@ export function getFilterContext(
|
|||
clickedLayers: LayerValue[],
|
||||
layerColumnIds: string[],
|
||||
table: KibanaDatatable
|
||||
): ValueClickTriggerContext {
|
||||
): LensFilterEvent['data'] {
|
||||
const matchingIndex = table.rows.findIndex(row =>
|
||||
clickedLayers.every((layer, index) => {
|
||||
const columnId = layerColumnIds[index];
|
||||
|
@ -37,13 +37,11 @@ export function getFilterContext(
|
|||
);
|
||||
|
||||
return {
|
||||
data: {
|
||||
data: clickedLayers.map((clickedLayer, index) => ({
|
||||
column: table.columns.findIndex(col => col.id === layerColumnIds[index]),
|
||||
row: matchingIndex,
|
||||
value: clickedLayer.groupByRollup,
|
||||
table,
|
||||
})),
|
||||
},
|
||||
data: clickedLayers.map((clickedLayer, index) => ({
|
||||
column: table.columns.findIndex(col => col.id === layerColumnIds[index]),
|
||||
row: matchingIndex,
|
||||
value: clickedLayer.groupByRollup,
|
||||
table,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -103,9 +103,6 @@ export class LensPlugin {
|
|||
|
||||
start(core: CoreStart, startDependencies: LensPluginStartDependencies) {
|
||||
this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance;
|
||||
this.xyVisualization.start(core, startDependencies);
|
||||
this.datatableVisualization.start(core, startDependencies);
|
||||
this.pieVisualization.start(core, startDependencies);
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
|
|
@ -1,12 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { createGetterSetter } from '../../../../src/plugins/kibana_utils/public';
|
||||
import { UiActionsStart } from '../../../../src/plugins/ui_actions/public';
|
||||
|
||||
export const [getExecuteTriggerActions, setExecuteTriggerActions] = createGetterSetter<
|
||||
UiActionsStart['executeTriggerActions']
|
||||
>('executeTriggerActions');
|
|
@ -7,11 +7,21 @@
|
|||
import { Ast } from '@kbn/interpreter/common';
|
||||
import { IconType } from '@elastic/eui/src/components/icon/icon';
|
||||
import { CoreSetup } from 'kibana/public';
|
||||
import { KibanaDatatable, SerializedFieldFormat } from '../../../../src/plugins/expressions/public';
|
||||
import {
|
||||
ExpressionRendererEvent,
|
||||
IInterpreterRenderHandlers,
|
||||
KibanaDatatable,
|
||||
SerializedFieldFormat,
|
||||
} from '../../../../src/plugins/expressions/public';
|
||||
import { DragContextState } from './drag_drop';
|
||||
import { Document } from './persistence';
|
||||
import { DateRange } from '../common';
|
||||
import { Query, Filter, SavedQuery, IFieldFormat } from '../../../../src/plugins/data/public';
|
||||
import {
|
||||
SELECT_RANGE_TRIGGER,
|
||||
TriggerContext,
|
||||
VALUE_CLICK_TRIGGER,
|
||||
} from '../../../../src/plugins/ui_actions/public';
|
||||
|
||||
export type ErrorCallback = (e: { message: string }) => void;
|
||||
|
||||
|
@ -467,3 +477,29 @@ export interface Visualization<T = unknown, P = unknown> {
|
|||
*/
|
||||
toPreviewExpression?: (state: T, frame: FramePublicAPI) => Ast | string | null;
|
||||
}
|
||||
|
||||
export interface LensFilterEvent {
|
||||
name: 'filter';
|
||||
data: TriggerContext<typeof VALUE_CLICK_TRIGGER>['data'];
|
||||
}
|
||||
export interface LensBrushEvent {
|
||||
name: 'brush';
|
||||
data: TriggerContext<typeof SELECT_RANGE_TRIGGER>['data'];
|
||||
}
|
||||
|
||||
export function isLensFilterEvent(event: ExpressionRendererEvent): event is LensFilterEvent {
|
||||
return event.name === 'filter';
|
||||
}
|
||||
|
||||
export function isLensBrushEvent(event: ExpressionRendererEvent): event is LensBrushEvent {
|
||||
return event.name === 'brush';
|
||||
}
|
||||
|
||||
/**
|
||||
* Expression renderer handlers specifically for lens renderers. This is a narrowed down
|
||||
* version of the general render handlers, specifying supported event types. If this type is
|
||||
* used, dispatched events will be handled correctly.
|
||||
*/
|
||||
export interface ILensInterpreterRenderHandlers extends IInterpreterRenderHandlers {
|
||||
event: (event: LensFilterEvent | LensBrushEvent) => void;
|
||||
}
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
*/
|
||||
|
||||
import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme';
|
||||
import { CoreSetup, IUiSettingsClient, CoreStart } from 'kibana/public';
|
||||
import { CoreSetup, IUiSettingsClient } from 'kibana/public';
|
||||
import moment from 'moment-timezone';
|
||||
import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public';
|
||||
import { xyVisualization } from './xy_visualization';
|
||||
import { xyChart, getXyChartRenderer } from './xy_expression';
|
||||
import { legendConfig, xConfig, layerConfig } from './types';
|
||||
import { EditorFrameSetup, FormatFactory } from '../types';
|
||||
import { setExecuteTriggerActions } from '../services';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
|
||||
export interface XyVisualizationPluginSetupPlugins {
|
||||
expressions: ExpressionsSetup;
|
||||
|
@ -21,10 +19,6 @@ export interface XyVisualizationPluginSetupPlugins {
|
|||
editorFrame: EditorFrameSetup;
|
||||
}
|
||||
|
||||
interface XyVisualizationPluginStartPlugins {
|
||||
uiActions: UiActionsStart;
|
||||
}
|
||||
|
||||
function getTimeZone(uiSettings: IUiSettingsClient) {
|
||||
const configuredTimeZone = uiSettings.get('dateFormat:tz');
|
||||
if (configuredTimeZone === 'Browser') {
|
||||
|
@ -59,7 +53,4 @@ export class XyVisualization {
|
|||
|
||||
editorFrame.registerVisualization(xyVisualization);
|
||||
}
|
||||
start(core: CoreStart, { uiActions }: XyVisualizationPluginStartPlugins) {
|
||||
setExecuteTriggerActions(uiActions.executeTriggerActions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ import { XYArgs, LegendConfig, legendConfig, layerConfig, LayerArgs } from './ty
|
|||
import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
|
||||
const executeTriggerActions = jest.fn();
|
||||
const onClickValue = jest.fn();
|
||||
const onSelectRange = jest.fn();
|
||||
|
||||
const dateHistogramData: LensMultiTable = {
|
||||
type: 'lens_multitable',
|
||||
|
@ -296,7 +297,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -344,7 +346,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(`
|
||||
|
@ -379,7 +382,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -415,7 +419,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -452,7 +457,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -496,7 +502,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -530,7 +537,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component.find(Settings).prop('xDomain')).toBeUndefined();
|
||||
|
@ -546,7 +554,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -563,7 +572,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -580,7 +590,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -602,7 +613,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -611,12 +623,10 @@ describe('xy_expression', () => {
|
|||
.first()
|
||||
.prop('onBrushEnd')!({ x: [1585757732783, 1585758880838] });
|
||||
|
||||
expect(executeTriggerActions).toHaveBeenCalledWith('SELECT_RANGE_TRIGGER', {
|
||||
data: {
|
||||
column: 0,
|
||||
table: dateHistogramData.tables.timeLayer,
|
||||
range: [1585757732783, 1585758880838],
|
||||
},
|
||||
expect(onSelectRange).toHaveBeenCalledWith({
|
||||
column: 0,
|
||||
table: dateHistogramData.tables.timeLayer,
|
||||
range: [1585757732783, 1585758880838],
|
||||
timeFieldName: 'order_date',
|
||||
});
|
||||
});
|
||||
|
@ -656,7 +666,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -665,23 +676,21 @@ describe('xy_expression', () => {
|
|||
.first()
|
||||
.prop('onElementClick')!([[geometry, series as XYChartSeriesIdentifier]]);
|
||||
|
||||
expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', {
|
||||
data: {
|
||||
data: [
|
||||
{
|
||||
column: 1,
|
||||
row: 1,
|
||||
table: data.tables.first,
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
column: 1,
|
||||
row: 0,
|
||||
table: data.tables.first,
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
expect(onClickValue).toHaveBeenCalledWith({
|
||||
data: [
|
||||
{
|
||||
column: 1,
|
||||
row: 1,
|
||||
table: data.tables.first,
|
||||
value: 5,
|
||||
},
|
||||
{
|
||||
column: 1,
|
||||
row: 0,
|
||||
table: data.tables.first,
|
||||
value: 2,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -695,7 +704,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -713,7 +723,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -734,7 +745,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -753,7 +765,8 @@ describe('xy_expression', () => {
|
|||
timeZone="CEST"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component.find(LineSeries).prop('timeZone')).toEqual('CEST');
|
||||
|
@ -771,7 +784,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(true);
|
||||
|
@ -796,7 +810,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(true);
|
||||
|
@ -815,7 +830,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component.find(BarSeries).prop('enableHistogramMode')).toEqual(false);
|
||||
|
@ -876,7 +892,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1071,7 +1088,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component.find(LineSeries).prop('xScaleType')).toEqual(ScaleType.Ordinal);
|
||||
|
@ -1088,7 +1106,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(component.find(LineSeries).prop('yScaleType')).toEqual(ScaleType.Sqrt);
|
||||
|
@ -1105,7 +1124,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -1123,7 +1143,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -1141,7 +1162,8 @@ describe('xy_expression', () => {
|
|||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
timeZone="UTC"
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
expect(getFormatSpy).toHaveBeenCalledWith({
|
||||
|
@ -1161,7 +1183,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -1248,7 +1271,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -1302,7 +1326,8 @@ describe('xy_expression', () => {
|
|||
timeZone="UTC"
|
||||
chartTheme={{}}
|
||||
histogramBarTarget={50}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -21,24 +21,22 @@ import {
|
|||
} from '@elastic/charts';
|
||||
import { I18nProvider } from '@kbn/i18n/react';
|
||||
import {
|
||||
IInterpreterRenderHandlers,
|
||||
ExpressionRenderDefinition,
|
||||
ExpressionFunctionDefinition,
|
||||
ExpressionRenderDefinition,
|
||||
ExpressionValueSearchContext,
|
||||
} from 'src/plugins/expressions/public';
|
||||
import { IconType } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
ValueClickTriggerContext,
|
||||
RangeSelectTriggerContext,
|
||||
} from '../../../../../src/plugins/embeddable/public';
|
||||
import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public';
|
||||
import { LensMultiTable, FormatFactory } from '../types';
|
||||
LensMultiTable,
|
||||
FormatFactory,
|
||||
ILensInterpreterRenderHandlers,
|
||||
LensFilterEvent,
|
||||
LensBrushEvent,
|
||||
} from '../types';
|
||||
import { XYArgs, SeriesType, visualizationTypes } from './types';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { isHorizontalChart } from './state_helpers';
|
||||
import { getExecuteTriggerActions } from '../services';
|
||||
import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public';
|
||||
import { parseInterval } from '../../../../../src/plugins/data/common';
|
||||
import { EmptyPlaceholder } from '../shared_components';
|
||||
|
||||
|
@ -63,7 +61,8 @@ type XYChartRenderProps = XYChartProps & {
|
|||
formatFactory: FormatFactory;
|
||||
timeZone: string;
|
||||
histogramBarTarget: number;
|
||||
executeTriggerActions: UiActionsStart['executeTriggerActions'];
|
||||
onClickValue: (data: LensFilterEvent['data']) => void;
|
||||
onSelectRange: (data: LensBrushEvent['data']) => void;
|
||||
};
|
||||
|
||||
export const xyChart: ExpressionFunctionDefinition<
|
||||
|
@ -125,9 +124,18 @@ export const getXyChartRenderer = (dependencies: {
|
|||
}),
|
||||
validate: () => undefined,
|
||||
reuseDomNode: true,
|
||||
render: async (domNode: Element, config: XYChartProps, handlers: IInterpreterRenderHandlers) => {
|
||||
const executeTriggerActions = getExecuteTriggerActions();
|
||||
render: async (
|
||||
domNode: Element,
|
||||
config: XYChartProps,
|
||||
handlers: ILensInterpreterRenderHandlers
|
||||
) => {
|
||||
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
|
||||
const onClickValue = (data: LensFilterEvent['data']) => {
|
||||
handlers.event({ name: 'filter', data });
|
||||
};
|
||||
const onSelectRange = (data: LensBrushEvent['data']) => {
|
||||
handlers.event({ name: 'brush', data });
|
||||
};
|
||||
const formatFactory = await dependencies.formatFactory;
|
||||
ReactDOM.render(
|
||||
<I18nProvider>
|
||||
|
@ -137,7 +145,8 @@ export const getXyChartRenderer = (dependencies: {
|
|||
chartTheme={dependencies.chartTheme}
|
||||
timeZone={dependencies.timeZone}
|
||||
histogramBarTarget={dependencies.histogramBarTarget}
|
||||
executeTriggerActions={executeTriggerActions}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
</I18nProvider>,
|
||||
domNode,
|
||||
|
@ -177,7 +186,8 @@ export function XYChart({
|
|||
timeZone,
|
||||
chartTheme,
|
||||
histogramBarTarget,
|
||||
executeTriggerActions,
|
||||
onClickValue,
|
||||
onSelectRange,
|
||||
}: XYChartRenderProps) {
|
||||
const { legend, layers } = args;
|
||||
|
||||
|
@ -287,15 +297,13 @@ export function XYChart({
|
|||
);
|
||||
const timeFieldName = table.columns[xAxisColumnIndex]?.meta?.aggConfigParams?.field;
|
||||
|
||||
const context: RangeSelectTriggerContext = {
|
||||
data: {
|
||||
range: [min, max],
|
||||
table,
|
||||
column: xAxisColumnIndex,
|
||||
},
|
||||
const context: LensBrushEvent['data'] = {
|
||||
range: [min, max],
|
||||
table,
|
||||
column: xAxisColumnIndex,
|
||||
timeFieldName,
|
||||
};
|
||||
executeTriggerActions(VIS_EVENT_TO_TRIGGER.brush, context);
|
||||
onSelectRange(context);
|
||||
}}
|
||||
onElementClick={([[geometry, series]]) => {
|
||||
// for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue
|
||||
|
@ -337,18 +345,16 @@ export function XYChart({
|
|||
?.aggConfigParams?.field;
|
||||
const timeFieldName = xDomain && xAxisFieldName;
|
||||
|
||||
const context: ValueClickTriggerContext = {
|
||||
data: {
|
||||
data: points.map(point => ({
|
||||
row: point.row,
|
||||
column: point.column,
|
||||
value: point.value,
|
||||
table,
|
||||
})),
|
||||
},
|
||||
const context: LensFilterEvent['data'] = {
|
||||
data: points.map(point => ({
|
||||
row: point.row,
|
||||
column: point.column,
|
||||
value: point.value,
|
||||
table,
|
||||
})),
|
||||
timeFieldName,
|
||||
};
|
||||
executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context);
|
||||
onClickValue(context);
|
||||
}}
|
||||
/>
|
||||
|
||||
|
|
Loading…
Reference in a new issue