mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Lens] Expose helpers to capture meta information from Lens state (#144546)
## Summary Closes: #131710 In order to add complimentary features to Lens embeddables via actions, it's important to be able to capture the relevant information from the state which is currently loaded. E.g. https://github.com/elastic/kibana/pull/129762 is pulling out the used field names from the Lens state. While the state interface is considered a public interface (as it's also used to configure Lens embeddables), it would be beneficial to provide use case specific helpers to extract this information to make this logic easier to maintain. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
1d511a439d
commit
545ebb012d
13 changed files with 597 additions and 4 deletions
|
@ -15,7 +15,7 @@ import type { DiscoverStart } from '@kbn/discover-plugin/public';
|
|||
import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { flatten, isEqual } from 'lodash';
|
||||
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import type { DataViewsPublicPluginStart, DataView } from '@kbn/data-views-plugin/public';
|
||||
import type { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
|
||||
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { DataPublicPluginStart, ES_FIELD_TYPES } from '@kbn/data-plugin/public';
|
||||
|
@ -37,6 +37,7 @@ import type {
|
|||
IndexPattern,
|
||||
IndexPatternRef,
|
||||
DatasourceLayerSettingsProps,
|
||||
DataSourceInfo,
|
||||
} from '../../types';
|
||||
import {
|
||||
changeIndexPattern,
|
||||
|
@ -105,7 +106,7 @@ export { deleteColumn } from './operations';
|
|||
export function columnToOperation(
|
||||
column: GenericIndexPatternColumn,
|
||||
uniqueLabel?: string,
|
||||
dataView?: IndexPattern
|
||||
dataView?: IndexPattern | DataView
|
||||
): OperationDescriptor {
|
||||
const { dataType, label, isBucketed, scale, operationType, timeShift, reducedTimeRange } = column;
|
||||
const fieldTypes =
|
||||
|
@ -984,6 +985,31 @@ export function getFormBasedDatasource({
|
|||
getUsedDataViews: (state) => {
|
||||
return Object.values(state.layers).map(({ indexPatternId }) => indexPatternId);
|
||||
},
|
||||
|
||||
getDatasourceInfo: (state, references, indexPatterns) => {
|
||||
const layers = references ? injectReferences(state, references).layers : state.layers;
|
||||
return Object.entries(layers).reduce<DataSourceInfo[]>((acc, [key, layer]) => {
|
||||
const dataView = indexPatterns?.find(
|
||||
(indexPattern) => indexPattern.id === layer.indexPatternId
|
||||
);
|
||||
|
||||
const columns = Object.entries(layer.columns).map(([colId, col]) => {
|
||||
return {
|
||||
id: colId,
|
||||
role: col.isBucketed ? ('split' as const) : ('metric' as const),
|
||||
operation: columnToOperation(col, undefined, dataView),
|
||||
};
|
||||
});
|
||||
|
||||
acc.push({
|
||||
layerId: key,
|
||||
columns,
|
||||
dataView,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
},
|
||||
};
|
||||
|
||||
return formBasedDatasource;
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
DataType,
|
||||
TableChangeType,
|
||||
DatasourceDimensionTriggerProps,
|
||||
DataSourceInfo,
|
||||
} from '../../types';
|
||||
import { generateId } from '../../id_generator';
|
||||
import { toExpression } from './to_expression';
|
||||
|
@ -706,6 +707,31 @@ export function getTextBasedDatasource({
|
|||
getDatasourceSuggestionsFromCurrentState: getSuggestionsForState,
|
||||
getDatasourceSuggestionsForVisualizeCharts: getSuggestionsForState,
|
||||
isEqual: () => true,
|
||||
getDatasourceInfo: (state, references, indexPatterns) => {
|
||||
return Object.entries(state.layers).reduce<DataSourceInfo[]>((acc, [key, layer]) => {
|
||||
const columns = Object.entries(layer.columns).map(([colId, col]) => {
|
||||
return {
|
||||
id: colId,
|
||||
role: col.meta?.type !== 'number' ? ('split' as const) : ('metric' as const),
|
||||
operation: {
|
||||
dataType: col?.meta?.type as DataType,
|
||||
label: col.fieldName,
|
||||
isBucketed: Boolean(col?.meta?.type !== 'number'),
|
||||
hasTimeShift: false,
|
||||
hasReducedTimeRange: false,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
acc.push({
|
||||
layerId: key,
|
||||
columns,
|
||||
dataView: indexPatterns?.find((dataView) => dataView.id === layer.index),
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
},
|
||||
};
|
||||
|
||||
return TextBasedDatasource;
|
||||
|
|
|
@ -29,7 +29,7 @@ import { LensAttributeService } from '../lens_attribute_service';
|
|||
import { OnSaveProps } from '@kbn/saved-objects-plugin/public/save_modal';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { inspectorPluginMock } from '@kbn/inspector-plugin/public/mocks';
|
||||
import { Visualization } from '../types';
|
||||
import { Datasource, Visualization } from '../types';
|
||||
|
||||
jest.mock('@kbn/inspector-plugin/public', () => ({
|
||||
isAvailable: false,
|
||||
|
@ -1499,4 +1499,129 @@ describe('embeddable', () => {
|
|||
expect(expressionRenderer).toHaveBeenCalledTimes(2);
|
||||
expect(expressionRenderer.mock.calls[1][0]!.padding).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should return chart info', async () => {
|
||||
expressionRenderer = jest.fn((_) => null);
|
||||
|
||||
const visDocument: Document = {
|
||||
state: {
|
||||
visualization: {},
|
||||
datasourceStates: {
|
||||
form_based: {},
|
||||
},
|
||||
query: { query: '', language: 'lucene' },
|
||||
filters: [],
|
||||
},
|
||||
references: [],
|
||||
title: 'My title',
|
||||
visualizationType: 'testVis',
|
||||
};
|
||||
const mockGetDatasourceInfo = jest.fn().mockReturnValue([
|
||||
{
|
||||
layerId: 'test',
|
||||
columns: [
|
||||
{
|
||||
id: '1',
|
||||
role: 'metric',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const mockGetVisualizationInfo = jest.fn().mockReturnValue({
|
||||
layers: [
|
||||
{
|
||||
layerId: 'test',
|
||||
dimensions: [
|
||||
{
|
||||
id: '1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const createEmbeddable = (noPadding?: boolean) => {
|
||||
return new Embeddable(
|
||||
{
|
||||
timefilter: dataPluginMock.createSetupContract().query.timefilter.timefilter,
|
||||
attributeService: attributeServiceMockFromSavedVis(visDocument),
|
||||
data: dataMock,
|
||||
expressionRenderer,
|
||||
basePath,
|
||||
dataViews: {} as DataViewsContract,
|
||||
capabilities: {
|
||||
canSaveDashboards: true,
|
||||
canSaveVisualizations: true,
|
||||
discover: {},
|
||||
navLinks: {},
|
||||
},
|
||||
inspector: inspectorPluginMock.createStartContract(),
|
||||
getTrigger,
|
||||
theme: themeServiceMock.createStartContract(),
|
||||
visualizationMap: {
|
||||
[visDocument.visualizationType as string]: {
|
||||
getDisplayOptions: () => ({
|
||||
noPadding: false,
|
||||
}),
|
||||
getVisualizationInfo: mockGetVisualizationInfo,
|
||||
} as unknown as Visualization,
|
||||
},
|
||||
datasourceMap: {
|
||||
form_based: {
|
||||
getDatasourceInfo: mockGetDatasourceInfo,
|
||||
} as unknown as Datasource,
|
||||
},
|
||||
injectFilterReferences: jest.fn(mockInjectFilterReferences),
|
||||
documentToExpression: () =>
|
||||
Promise.resolve({
|
||||
ast: {
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{ type: 'function', function: 'my', arguments: {} },
|
||||
{ type: 'function', function: 'expression', arguments: {} },
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
}),
|
||||
uiSettings: { get: () => undefined } as unknown as IUiSettingsClient,
|
||||
},
|
||||
{
|
||||
timeRange: {
|
||||
from: 'now-15m',
|
||||
to: 'now',
|
||||
},
|
||||
noPadding,
|
||||
} as LensEmbeddableInput
|
||||
);
|
||||
};
|
||||
|
||||
const embeddable = createEmbeddable();
|
||||
|
||||
await embeddable.initializeSavedVis({ id: '123' } as LensEmbeddableInput);
|
||||
|
||||
const chartInfo = embeddable.getChartInfo();
|
||||
|
||||
expect(mockGetVisualizationInfo).toHaveBeenCalledTimes(1);
|
||||
expect(mockGetDatasourceInfo).toHaveBeenCalledTimes(1);
|
||||
expect(chartInfo).toEqual({
|
||||
filters: [],
|
||||
layers: [
|
||||
{
|
||||
dataView: undefined,
|
||||
dimensions: [
|
||||
{
|
||||
id: '1',
|
||||
role: 'metric',
|
||||
},
|
||||
],
|
||||
layerId: 'test',
|
||||
},
|
||||
],
|
||||
query: {
|
||||
language: 'lucene',
|
||||
query: '',
|
||||
},
|
||||
visualizationType: 'testVis',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
TimeRange,
|
||||
isOfQueryType,
|
||||
} from '@kbn/es-query';
|
||||
import type { IconType } from '@elastic/eui/src/components/icon/icon';
|
||||
import type { PaletteOutput } from '@kbn/coloring';
|
||||
import {
|
||||
DataPublicPluginStart,
|
||||
|
@ -79,6 +80,7 @@ import {
|
|||
DatasourceMap,
|
||||
Datasource,
|
||||
IndexPatternMap,
|
||||
OperationDescriptor,
|
||||
} from '../types';
|
||||
|
||||
import { getEditPath, DOC_TYPE } from '../../common';
|
||||
|
@ -106,6 +108,28 @@ export interface LensUnwrapResult {
|
|||
metaInfo?: LensUnwrapMetaInfo;
|
||||
}
|
||||
|
||||
interface ChartInfo {
|
||||
layers: ChartLayerDescriptor[];
|
||||
visualizationType: string;
|
||||
filters: Document['state']['filters'];
|
||||
query: Document['state']['query'];
|
||||
}
|
||||
|
||||
export interface ChartLayerDescriptor {
|
||||
dataView?: DataView;
|
||||
layerId: string;
|
||||
layerType: string;
|
||||
chartType?: string;
|
||||
icon?: IconType;
|
||||
label?: string;
|
||||
dimensions: Array<{
|
||||
name: string;
|
||||
id: string;
|
||||
role: 'split' | 'metric';
|
||||
operation: OperationDescriptor;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface LensBaseEmbeddableInput extends EmbeddableInput {
|
||||
filters?: Filter[];
|
||||
query?: Query;
|
||||
|
@ -493,6 +517,7 @@ export class Embeddable
|
|||
this.errors = this.maybeAddConflictError(errors, metaInfo?.sharingSavedObjectProps);
|
||||
|
||||
await this.initializeOutput();
|
||||
|
||||
this.isInitialized = true;
|
||||
}
|
||||
|
||||
|
@ -1080,6 +1105,46 @@ export class Embeddable
|
|||
};
|
||||
}
|
||||
|
||||
public getChartInfo(): Readonly<ChartInfo | undefined> {
|
||||
const activeDatasourceId = getActiveDatasourceIdFromDoc(this.savedVis);
|
||||
if (!activeDatasourceId || !this.savedVis?.visualizationType) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const docDatasourceState = this.savedVis?.state.datasourceStates[activeDatasourceId];
|
||||
const dataSourceInfo = this.deps.datasourceMap[activeDatasourceId].getDatasourceInfo(
|
||||
docDatasourceState,
|
||||
this.savedVis?.references,
|
||||
this.indexPatterns
|
||||
);
|
||||
const chartInfo = this.deps.visualizationMap[
|
||||
this.savedVis.visualizationType
|
||||
].getVisualizationInfo?.(this.savedVis?.state.visualization);
|
||||
|
||||
const layers = chartInfo?.layers.map((l) => {
|
||||
const dataSource = dataSourceInfo.find((info) => info.layerId === l.layerId);
|
||||
const updatedDimensions = l.dimensions.map((d) => {
|
||||
return {
|
||||
...d,
|
||||
...dataSource?.columns.find((c) => c.id === d.id)!,
|
||||
};
|
||||
});
|
||||
return {
|
||||
...l,
|
||||
dataView: dataSource?.dataView,
|
||||
dimensions: updatedDimensions,
|
||||
};
|
||||
});
|
||||
return layers
|
||||
? {
|
||||
layers,
|
||||
visualizationType: this.savedVis.visualizationType,
|
||||
filters: this.savedVis.state.filters,
|
||||
query: this.savedVis.state.query,
|
||||
}
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private get visDisplayOptions(): VisualizationDisplayOptions | undefined {
|
||||
if (
|
||||
!this.savedVis?.visualizationType ||
|
||||
|
|
|
@ -67,6 +67,7 @@ export function createMockDatasource(id: string): DatasourceMock {
|
|||
getUsedDataView: jest.fn((state, layer) => 'mockip'),
|
||||
getUsedDataViews: jest.fn(),
|
||||
onRefreshIndexPattern: jest.fn(),
|
||||
getDatasourceInfo: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import type {
|
|||
} from '@kbn/ui-actions-plugin/public';
|
||||
import type { ClickTriggerEvent, BrushTriggerEvent } from '@kbn/charts-plugin/public';
|
||||
import type { IndexPatternAggRestrictions } from '@kbn/data-plugin/public';
|
||||
import type { FieldSpec, DataViewSpec } from '@kbn/data-views-plugin/common';
|
||||
import type { FieldSpec, DataViewSpec, DataView } from '@kbn/data-views-plugin/common';
|
||||
import type { FieldFormatParams } from '@kbn/field-formats-plugin/common';
|
||||
import { SearchResponseWarning } from '@kbn/data-plugin/public/search/types';
|
||||
import type { EuiButtonIconProps } from '@elastic/eui';
|
||||
|
@ -134,6 +134,23 @@ export interface TableSuggestionColumn {
|
|||
operation: Operation;
|
||||
}
|
||||
|
||||
export interface DataSourceInfo {
|
||||
layerId: string;
|
||||
dataView?: DataView;
|
||||
columns: Array<{ id: string; role: 'split' | 'metric'; operation: OperationDescriptor }>;
|
||||
}
|
||||
|
||||
export interface VisualizationInfo {
|
||||
layers: Array<{
|
||||
layerId: string;
|
||||
layerType: string;
|
||||
chartType?: string;
|
||||
icon?: IconType;
|
||||
label?: string;
|
||||
dimensions: Array<{ name: string; id: string }>;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A possible table a datasource can create. This object is passed to the visualization
|
||||
* which tries to build a meaningful visualization given the shape of the table. If this
|
||||
|
@ -478,6 +495,12 @@ export interface Datasource<T = unknown, P = unknown> {
|
|||
setState: StateSetter<T>,
|
||||
openLayerSettings?: () => void
|
||||
) => LayerAction[];
|
||||
|
||||
getDatasourceInfo: (
|
||||
state: T,
|
||||
references?: SavedObjectReference[],
|
||||
dataViews?: DataView[]
|
||||
) => DataSourceInfo[];
|
||||
}
|
||||
|
||||
export interface DatasourceFixAction<T> {
|
||||
|
@ -1247,6 +1270,8 @@ export interface Visualization<T = unknown, P = unknown> {
|
|||
getSuggestionFromConvertToLensContext?: (
|
||||
props: VisualizationStateFromContextChangeProps
|
||||
) => Suggestion<T> | undefined;
|
||||
|
||||
getVisualizationInfo?: (state: T) => VisualizationInfo;
|
||||
}
|
||||
|
||||
// Use same technique as TriggerContext
|
||||
|
|
|
@ -613,6 +613,39 @@ export const getDatatableVisualization = ({
|
|||
};
|
||||
return suggestion;
|
||||
},
|
||||
|
||||
getVisualizationInfo(state: DatatableVisualizationState) {
|
||||
return {
|
||||
layers: [
|
||||
{
|
||||
layerId: state.layerId,
|
||||
layerType: state.layerType,
|
||||
chartType: 'table',
|
||||
...this.getDescription(state),
|
||||
dimensions: state.columns.map((column) => {
|
||||
let name = i18n.translate('xpack.lens.datatable.metric', {
|
||||
defaultMessage: 'Metric',
|
||||
});
|
||||
if (!column.transposable) {
|
||||
if (column.isTransposed) {
|
||||
name = i18n.translate('xpack.lens.datatable.breakdownColumns', {
|
||||
defaultMessage: 'Split metrics by',
|
||||
});
|
||||
} else {
|
||||
name = i18n.translate('xpack.lens.datatable.breakdownRow', {
|
||||
defaultMessage: 'Row',
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
id: column.columnId,
|
||||
name,
|
||||
};
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
function getDataSourceAndSortedColumns(
|
||||
|
|
|
@ -556,4 +556,54 @@ export const getGaugeVisualization = ({
|
|||
};
|
||||
return suggestion;
|
||||
},
|
||||
|
||||
getVisualizationInfo(state: GaugeVisualizationState) {
|
||||
const dimensions = [];
|
||||
if (state.metricAccessor) {
|
||||
dimensions.push({
|
||||
id: state.metricAccessor,
|
||||
name: i18n.translate('xpack.lens.gauge.metricLabel', {
|
||||
defaultMessage: 'Metric',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.maxAccessor) {
|
||||
dimensions.push({
|
||||
id: state.maxAccessor,
|
||||
name: i18n.translate('xpack.lens.gauge.maxValueLabel', {
|
||||
defaultMessage: 'Maximum value',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.minAccessor) {
|
||||
dimensions.push({
|
||||
id: state.minAccessor,
|
||||
name: i18n.translate('xpack.lens.gauge.minValueLabel', {
|
||||
defaultMessage: 'Minimum value',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.goalAccessor) {
|
||||
dimensions.push({
|
||||
id: state.goalAccessor,
|
||||
name: i18n.translate('xpack.lens.gauge.goalValueLabel', {
|
||||
defaultMessage: 'Goal value',
|
||||
}),
|
||||
});
|
||||
}
|
||||
return {
|
||||
layers: [
|
||||
{
|
||||
layerId: state.layerId,
|
||||
layerType: state.layerType,
|
||||
chartType: state.shape,
|
||||
...this.getDescription(state),
|
||||
dimensions,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -507,4 +507,42 @@ export const getHeatmapVisualization = ({
|
|||
};
|
||||
return suggestion;
|
||||
},
|
||||
|
||||
getVisualizationInfo(state: HeatmapVisualizationState) {
|
||||
const dimensions = [];
|
||||
if (state.xAccessor) {
|
||||
dimensions.push({
|
||||
id: state.xAccessor,
|
||||
name: getAxisName(GROUP_ID.X),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.yAccessor) {
|
||||
dimensions.push({
|
||||
id: state.yAccessor,
|
||||
name: getAxisName(GROUP_ID.Y),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.valueAccessor) {
|
||||
dimensions.push({
|
||||
id: state.valueAccessor,
|
||||
name: i18n.translate('xpack.lens.heatmap.cellValueLabel', {
|
||||
defaultMessage: 'Cell value',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
layers: [
|
||||
{
|
||||
layerId: state.layerId,
|
||||
layerType: state.layerType,
|
||||
chartType: state.shape,
|
||||
...this.getDescription(state),
|
||||
dimensions,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -316,4 +316,28 @@ export const getLegacyMetricVisualization = ({
|
|||
// Is it possible to break it?
|
||||
return undefined;
|
||||
},
|
||||
|
||||
getVisualizationInfo(state: LegacyMetricState) {
|
||||
const dimensions = [];
|
||||
if (state.accessor) {
|
||||
dimensions.push({
|
||||
id: state.accessor,
|
||||
name: i18n.translate('xpack.lens.metric.label', {
|
||||
defaultMessage: 'Metric',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
layers: [
|
||||
{
|
||||
layerId: state.layerId,
|
||||
layerType: state.layerType,
|
||||
chartType: 'metric',
|
||||
...this.getDescription(state),
|
||||
dimensions,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -669,4 +669,53 @@ export const getMetricVisualization = ({
|
|||
};
|
||||
return suggestion;
|
||||
},
|
||||
|
||||
getVisualizationInfo(state: MetricVisualizationState) {
|
||||
const dimensions = [];
|
||||
if (state.metricAccessor) {
|
||||
dimensions.push({
|
||||
id: state.metricAccessor,
|
||||
name: i18n.translate('xpack.lens.primaryMetric.label', {
|
||||
defaultMessage: 'Primary metric',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.secondaryMetricAccessor) {
|
||||
dimensions.push({
|
||||
id: state.secondaryMetricAccessor,
|
||||
name: i18n.translate('xpack.lens.metric.secondaryMetric', {
|
||||
defaultMessage: 'Secondary metric',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.maxAccessor) {
|
||||
dimensions.push({
|
||||
id: state.maxAccessor,
|
||||
name: i18n.translate('xpack.lens.metric.max', { defaultMessage: 'Maximum value' }),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.breakdownByAccessor) {
|
||||
dimensions.push({
|
||||
id: state.breakdownByAccessor,
|
||||
name: i18n.translate('xpack.lens.metric.breakdownBy', {
|
||||
defaultMessage: 'Break down by',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
layers: [
|
||||
{
|
||||
layerId: state.layerId,
|
||||
layerType: state.layerType,
|
||||
chartType: 'metric',
|
||||
...this.getDescription(state),
|
||||
dimensions,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -508,4 +508,62 @@ export const getPieVisualization = ({
|
|||
]
|
||||
: [];
|
||||
},
|
||||
|
||||
getVisualizationInfo(state: PieVisualizationState) {
|
||||
const layer = state.layers[0];
|
||||
const dimensions = [];
|
||||
if (layer.metric) {
|
||||
dimensions.push({
|
||||
id: layer.metric,
|
||||
name: i18n.translate('xpack.lens.pie.groupsizeLabel', {
|
||||
defaultMessage: 'Size by',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (state.shape === 'mosaic' && layer.secondaryGroups && layer.secondaryGroups.length) {
|
||||
layer.secondaryGroups.forEach((accessor) => {
|
||||
dimensions.push({
|
||||
name: i18n.translate('xpack.lens.pie.horizontalAxisLabel', {
|
||||
defaultMessage: 'Horizontal axis',
|
||||
}),
|
||||
id: accessor,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (layer.primaryGroups && layer.primaryGroups.length) {
|
||||
let name = i18n.translate('xpack.lens.pie.treemapGroupLabel', {
|
||||
defaultMessage: 'Group by',
|
||||
});
|
||||
if (state.shape === 'mosaic') {
|
||||
name = i18n.translate('xpack.lens.pie.verticalAxisLabel', {
|
||||
defaultMessage: 'Vertical axis',
|
||||
});
|
||||
}
|
||||
if (state.shape === 'donut' || state.shape === 'pie') {
|
||||
name = i18n.translate('xpack.lens.pie.sliceGroupLabel', {
|
||||
defaultMessage: 'Slice by',
|
||||
});
|
||||
}
|
||||
layer.primaryGroups.forEach((accessor) => {
|
||||
dimensions.push({
|
||||
name,
|
||||
id: accessor,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
layers: [
|
||||
{
|
||||
layerId: layer.layerId,
|
||||
layerType: layer.layerType,
|
||||
chartType: state.shape,
|
||||
...this.getDescription(state),
|
||||
dimensions,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import { Position } from '@elastic/charts';
|
|||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { PaletteRegistry } from '@kbn/coloring';
|
||||
import { IconChartBarReferenceLine, IconChartBarAnnotations } from '@kbn/chart-icons';
|
||||
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { CoreStart, ThemeServiceStart } from '@kbn/core/public';
|
||||
import type { EventAnnotationServiceType } from '@kbn/event-annotation-plugin/public';
|
||||
|
@ -876,6 +877,78 @@ export const getXyVisualization = ({
|
|||
};
|
||||
return suggestion;
|
||||
},
|
||||
|
||||
getVisualizationInfo(state: XYState) {
|
||||
const isHorizontal = isHorizontalChart(state.layers);
|
||||
const visualizationLayersInfo = state.layers.map((layer) => {
|
||||
const dimensions = [];
|
||||
let chartType: SeriesType | undefined;
|
||||
let icon;
|
||||
let label;
|
||||
if (isDataLayer(layer)) {
|
||||
chartType = layer.seriesType;
|
||||
const layerVisType = visualizationTypes.find((visType) => visType.id === chartType);
|
||||
icon = layerVisType?.icon;
|
||||
label = layerVisType?.fullLabel || layerVisType?.label;
|
||||
if (layer.xAccessor) {
|
||||
dimensions.push({ name: getAxisName('x', { isHorizontal }), id: layer.xAccessor });
|
||||
}
|
||||
if (layer.accessors && layer.accessors.length) {
|
||||
layer.accessors.forEach((accessor) => {
|
||||
dimensions.push({ name: getAxisName('y', { isHorizontal }), id: accessor });
|
||||
});
|
||||
}
|
||||
if (layer.splitAccessor) {
|
||||
dimensions.push({
|
||||
name: i18n.translate('xpack.lens.xyChart.splitSeries', {
|
||||
defaultMessage: 'Breakdown',
|
||||
}),
|
||||
id: layer.splitAccessor,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isReferenceLayer(layer) && layer.accessors && layer.accessors.length) {
|
||||
layer.accessors.forEach((accessor) => {
|
||||
dimensions.push({
|
||||
name: i18n.translate('xpack.lens.xyChart.layerReferenceLine', {
|
||||
defaultMessage: 'Reference line',
|
||||
}),
|
||||
id: accessor,
|
||||
});
|
||||
});
|
||||
label = i18n.translate('xpack.lens.xyChart.layerReferenceLineLabel', {
|
||||
defaultMessage: 'Reference lines',
|
||||
});
|
||||
icon = IconChartBarReferenceLine;
|
||||
}
|
||||
if (isAnnotationsLayer(layer) && layer.annotations && layer.annotations.length) {
|
||||
layer.annotations.forEach((annotation) => {
|
||||
dimensions.push({
|
||||
name: i18n.translate('xpack.lens.xyChart.layerAnnotation', {
|
||||
defaultMessage: 'Annotation',
|
||||
}),
|
||||
id: annotation.id,
|
||||
});
|
||||
});
|
||||
label = i18n.translate('xpack.lens.xyChart.layerAnnotationsLabel', {
|
||||
defaultMessage: 'Annotations',
|
||||
});
|
||||
icon = IconChartBarAnnotations;
|
||||
}
|
||||
|
||||
return {
|
||||
layerId: layer.layerId,
|
||||
layerType: layer.layerType,
|
||||
chartType,
|
||||
icon,
|
||||
label,
|
||||
dimensions,
|
||||
};
|
||||
});
|
||||
return {
|
||||
layers: visualizationLayersInfo,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const getMappedAccessors = ({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue