mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Heatmap small multiples (#154434)
## Summary Enables split chart in agg based heatmaps (small multiples) and makes the EC implementation the default. It also marks the advanced setting that changes the implementation as deprecated. <img width="2493" alt="image" src="https://user-images.githubusercontent.com/17003240/230048435-f8b20240-5005-4343-ba18-e2c7307945e0.png"> ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Marco Vettorello <marco.vettorello@elastic.co> Co-authored-by: Nick Partridge <nick.ryan.partridge@gmail.com> Co-authored-by: nickofthyme <nicholas.partridge@elastic.co>
This commit is contained in:
parent
40d730b2ed
commit
544f908fd6
20 changed files with 338 additions and 109 deletions
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Accessor, AccessorFn, GroupBy, SmallMultiples, Predicate } from '@elastic/charts';
|
||||
|
||||
interface ChartSplitProps {
|
||||
splitColumnAccessor?: Accessor | AccessorFn;
|
||||
splitRowAccessor?: Accessor | AccessorFn;
|
||||
}
|
||||
|
||||
const CHART_SPLIT_ID = '__heatmap_chart_split__';
|
||||
const SMALL_MULTIPLES_ID = '__heatmap_chart_sm__';
|
||||
|
||||
export const ChartSplit = ({ splitColumnAccessor, splitRowAccessor }: ChartSplitProps) => {
|
||||
if (!splitColumnAccessor && !splitRowAccessor) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<GroupBy
|
||||
id={CHART_SPLIT_ID}
|
||||
by={(spec, datum) => {
|
||||
const splitTypeAccessor = splitColumnAccessor || splitRowAccessor;
|
||||
if (splitTypeAccessor) {
|
||||
return typeof splitTypeAccessor === 'function'
|
||||
? splitTypeAccessor(datum)
|
||||
: datum[splitTypeAccessor];
|
||||
}
|
||||
return spec.id;
|
||||
}}
|
||||
sort={Predicate.DataIndex}
|
||||
/>
|
||||
<SmallMultiples
|
||||
id={SMALL_MULTIPLES_ID}
|
||||
splitVertically={splitRowAccessor ? CHART_SPLIT_ID : undefined}
|
||||
splitHorizontally={splitColumnAccessor ? CHART_SPLIT_ID : undefined}
|
||||
style={{
|
||||
verticalPanelPadding: {
|
||||
outer: 0,
|
||||
inner: 0.1,
|
||||
},
|
||||
horizontalPanelPadding: {
|
||||
outer: 0,
|
||||
inner: 0.1,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -50,6 +50,8 @@ import {
|
|||
LegendColorPickerWrapper,
|
||||
} from '../utils/get_color_picker';
|
||||
import { defaultPaletteParams } from '../constants';
|
||||
import { ChartSplit } from './chart_split';
|
||||
import { getSplitDimensionAccessor, createSplitPoint } from '../utils/get_split_dimension_utils';
|
||||
import './index.scss';
|
||||
|
||||
declare global {
|
||||
|
@ -207,7 +209,6 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
|
|||
() => findMinMaxByColumnId([valueAccessor!], table),
|
||||
[valueAccessor, table]
|
||||
);
|
||||
|
||||
const paletteParams = args.palette?.params;
|
||||
const xAccessor = args.xAccessor
|
||||
? getAccessorByDimension(args.xAccessor, table.columns)
|
||||
|
@ -215,6 +216,12 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
|
|||
const yAccessor = args.yAccessor
|
||||
? getAccessorByDimension(args.yAccessor, table.columns)
|
||||
: undefined;
|
||||
const splitChartRowAccessor = args.splitRowAccessor
|
||||
? getSplitDimensionAccessor(data.columns, args.splitRowAccessor, formatFactory)
|
||||
: undefined;
|
||||
const splitChartColumnAccessor = args.splitColumnAccessor
|
||||
? getSplitDimensionAccessor(data.columns, args.splitColumnAccessor, formatFactory)
|
||||
: undefined;
|
||||
|
||||
const xAxisColumnIndex = table.columns.findIndex((v) => v.id === xAccessor);
|
||||
const yAxisColumnIndex = table.columns.findIndex((v) => v.id === yAccessor);
|
||||
|
@ -252,7 +259,7 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
|
|||
const onElementClick = useCallback(
|
||||
(e: HeatmapElementEvent[]) => {
|
||||
const cell = e[0][0];
|
||||
const { x, y } = cell.datum;
|
||||
const { x, y, smVerticalAccessorValue, smHorizontalAccessorValue } = cell.datum;
|
||||
|
||||
const points = [
|
||||
{
|
||||
|
@ -284,6 +291,28 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
|
|||
: []),
|
||||
];
|
||||
|
||||
if (smHorizontalAccessorValue && args.splitColumnAccessor) {
|
||||
const point = createSplitPoint(
|
||||
args.splitColumnAccessor,
|
||||
smHorizontalAccessorValue,
|
||||
formatFactory,
|
||||
table
|
||||
);
|
||||
if (point) {
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
if (smVerticalAccessorValue && args.splitRowAccessor) {
|
||||
const point = createSplitPoint(
|
||||
args.splitRowAccessor,
|
||||
smVerticalAccessorValue,
|
||||
formatFactory,
|
||||
table
|
||||
);
|
||||
if (point) {
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
const context: FilterEvent['data'] = {
|
||||
data: points.map((point) => ({
|
||||
row: point.row,
|
||||
|
@ -295,6 +324,9 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
|
|||
onClickValue(context);
|
||||
},
|
||||
[
|
||||
args.splitColumnAccessor,
|
||||
args.splitRowAccessor,
|
||||
formatFactory,
|
||||
formattedTable.formattedColumns,
|
||||
onClickValue,
|
||||
table,
|
||||
|
@ -579,6 +611,10 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = memo(
|
|||
}}
|
||||
>
|
||||
<Chart ref={chartRef}>
|
||||
<ChartSplit
|
||||
splitColumnAccessor={splitChartColumnAccessor}
|
||||
splitRowAccessor={splitChartRowAccessor}
|
||||
/>
|
||||
<Settings
|
||||
onRenderChange={onRenderChange}
|
||||
noResults={
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks';
|
||||
import type { DatatableColumn, Datatable } from '@kbn/expressions-plugin/public';
|
||||
import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
|
||||
import { getSplitDimensionAccessor, createSplitPoint } from './get_split_dimension_utils';
|
||||
|
||||
const data: Datatable = {
|
||||
type: 'datatable',
|
||||
rows: [
|
||||
{ 'col-0-1': 0, 'col-1-2': 'a', 'col-2-3': 'd' },
|
||||
{ 'col-0-1': 148, 'col-1-2': 'b', 'col-2-3': 'c' },
|
||||
],
|
||||
columns: [
|
||||
{ id: 'col-0-1', name: 'Count', meta: { type: 'number' } },
|
||||
{ id: 'col-1-2', name: 'Dest', meta: { type: 'string' } },
|
||||
{
|
||||
id: 'col-2-3',
|
||||
name: 'Test',
|
||||
meta: {
|
||||
type: 'number',
|
||||
params: {
|
||||
id: 'number',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('getSplitDimensionAccessor', () => {
|
||||
const defaultFormatter = jest.fn((...args) => fieldFormatsMock.deserialize(...args));
|
||||
|
||||
beforeEach(() => {
|
||||
defaultFormatter.mockClear();
|
||||
});
|
||||
|
||||
const splitDimension: ExpressionValueVisDimension = {
|
||||
type: 'vis_dimension',
|
||||
accessor: {
|
||||
id: data.columns[2].id,
|
||||
name: data.columns[2].name,
|
||||
meta: data.columns[2].meta,
|
||||
},
|
||||
format: {
|
||||
params: {},
|
||||
},
|
||||
};
|
||||
|
||||
it('returns accessor which is using formatter, if meta.params are present at accessing column', () => {
|
||||
const accessor = getSplitDimensionAccessor(data.columns, splitDimension, defaultFormatter);
|
||||
|
||||
expect(defaultFormatter).toHaveBeenCalledTimes(1);
|
||||
expect(typeof accessor).toBe('function');
|
||||
accessor(data.rows[0]);
|
||||
});
|
||||
|
||||
it('returns accessor which is using default formatter, if meta.params and format are not present', () => {
|
||||
const column: Partial<DatatableColumn> = {
|
||||
...data.columns[2],
|
||||
meta: { type: 'number' },
|
||||
};
|
||||
const columns = [data.columns[0], column, data.columns[2]] as DatatableColumn[];
|
||||
const defaultFormatterReturnedVal = fieldFormatsMock.deserialize();
|
||||
const spyOnDefaultFormatterConvert = jest.spyOn(defaultFormatterReturnedVal, 'convert');
|
||||
|
||||
defaultFormatter.mockReturnValueOnce(defaultFormatterReturnedVal);
|
||||
const accessor = getSplitDimensionAccessor(columns, splitDimension, defaultFormatter);
|
||||
|
||||
expect(defaultFormatter).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(typeof accessor).toBe('function');
|
||||
accessor(data.rows[0]);
|
||||
expect(spyOnDefaultFormatterConvert).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns accessor which returns undefined, if such column is not present', () => {
|
||||
const accessor1 = getSplitDimensionAccessor(data.columns, splitDimension, defaultFormatter);
|
||||
|
||||
expect(typeof accessor1).toBe('function');
|
||||
const result1 = accessor1({});
|
||||
expect(result1).toBeUndefined();
|
||||
|
||||
const column2: Partial<DatatableColumn> = {
|
||||
...data.columns[2],
|
||||
meta: { type: 'string' },
|
||||
};
|
||||
const columns2 = [data.columns[0], data.columns[1], column2] as DatatableColumn[];
|
||||
const accessor2 = getSplitDimensionAccessor(columns2, splitDimension, defaultFormatter);
|
||||
|
||||
expect(typeof accessor2).toBe('function');
|
||||
const result2 = accessor1({});
|
||||
expect(result2).toBeUndefined();
|
||||
|
||||
const column3 = {
|
||||
...data.columns[2],
|
||||
meta: { type: 'string' },
|
||||
format: {
|
||||
id: 'string',
|
||||
params: {},
|
||||
},
|
||||
};
|
||||
const columns3 = [data.columns[0], data.columns[1], column3] as DatatableColumn[];
|
||||
|
||||
const accessor3 = getSplitDimensionAccessor(columns3, splitDimension, defaultFormatter);
|
||||
expect(typeof accessor3).toBe('function');
|
||||
const result3 = accessor3({});
|
||||
expect(result3).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('createSplitPoint', () => {
|
||||
const defaultFormatter = jest.fn((...args) => fieldFormatsMock.deserialize(...args));
|
||||
|
||||
beforeEach(() => {
|
||||
defaultFormatter.mockClear();
|
||||
});
|
||||
|
||||
const splitDimension: ExpressionValueVisDimension = {
|
||||
type: 'vis_dimension',
|
||||
accessor: {
|
||||
id: data.columns[2].id,
|
||||
name: data.columns[2].name,
|
||||
meta: data.columns[2].meta,
|
||||
},
|
||||
format: {
|
||||
params: {},
|
||||
},
|
||||
};
|
||||
|
||||
it('returns point if value is found in the table', () => {
|
||||
const point = createSplitPoint(splitDimension, 'c', defaultFormatter, data);
|
||||
|
||||
expect(defaultFormatter).toHaveBeenCalledTimes(1);
|
||||
expect(point).toStrictEqual({ column: 2, row: 1, value: 'c' });
|
||||
});
|
||||
|
||||
it('returns undefined if value is not found in the table', () => {
|
||||
const point = createSplitPoint(splitDimension, 'test', defaultFormatter, data);
|
||||
|
||||
expect(defaultFormatter).toHaveBeenCalledTimes(1);
|
||||
expect(point).toBeUndefined();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import type { AccessorFn } from '@elastic/charts';
|
||||
import type { DatatableColumn, Datatable } from '@kbn/expressions-plugin/public';
|
||||
import type { FormatFactory } from '@kbn/field-formats-plugin/common';
|
||||
import type { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
|
||||
import { getColumnByAccessor } from '@kbn/visualizations-plugin/common/utils';
|
||||
|
||||
export const getSplitDimensionAccessor = (
|
||||
columns: DatatableColumn[],
|
||||
splitDimension: ExpressionValueVisDimension | string,
|
||||
formatFactory: FormatFactory
|
||||
): AccessorFn => {
|
||||
const splitChartColumn = getColumnByAccessor(splitDimension, columns)!;
|
||||
const accessor = splitChartColumn.id;
|
||||
|
||||
const formatter = formatFactory(splitChartColumn.meta?.params);
|
||||
const fn: AccessorFn = (d) => {
|
||||
const v = d[accessor];
|
||||
if (v === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const f = formatter.convert(v);
|
||||
return f;
|
||||
};
|
||||
|
||||
return fn;
|
||||
};
|
||||
|
||||
export function createSplitPoint(
|
||||
splitDimension: ExpressionValueVisDimension | string,
|
||||
value: string | number,
|
||||
formatFactory: FormatFactory,
|
||||
table: Datatable
|
||||
) {
|
||||
const splitChartColumn = getColumnByAccessor(splitDimension, table.columns)!;
|
||||
const accessor = splitChartColumn.id;
|
||||
|
||||
const formatter = formatFactory(splitChartColumn.meta?.params);
|
||||
const splitPointRowIndex = table.rows.findIndex((row) => {
|
||||
return formatter.convert(row[accessor]) === value;
|
||||
});
|
||||
if (splitPointRowIndex !== -1) {
|
||||
return {
|
||||
row: splitPointRowIndex,
|
||||
column: table.columns.findIndex((column) => column.id === accessor),
|
||||
value: table.rows[splitPointRowIndex][accessor],
|
||||
};
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Position } from '@elastic/charts';
|
||||
|
||||
|
@ -15,7 +14,6 @@ import { VIS_EVENT_TO_TRIGGER, VisTypeDefinition } from '@kbn/visualizations-plu
|
|||
import { HeatmapTypeProps, HeatmapVisParams, AxisType, ScaleType } from '../types';
|
||||
import { toExpressionAst } from '../to_ast';
|
||||
import { getHeatmapOptions } from '../editor/components';
|
||||
import { SplitTooltip } from './split_tooltip';
|
||||
import { convertToLens } from '../convert_to_lens';
|
||||
|
||||
export const getHeatmapVisTypeDefinition = ({
|
||||
|
@ -129,11 +127,6 @@ export const getHeatmapVisTypeDefinition = ({
|
|||
{
|
||||
group: AggGroupNames.Buckets,
|
||||
name: 'split',
|
||||
// TODO: Remove when split chart aggs are supported
|
||||
...(showElasticChartsOptions && {
|
||||
disabled: true,
|
||||
tooltip: <SplitTooltip />,
|
||||
}),
|
||||
title: i18n.translate('visTypeHeatmap.heatmap.splitTitle', {
|
||||
defaultMessage: 'Split chart',
|
||||
}),
|
|
@ -1,19 +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
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
export function SplitTooltip() {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="visTypeHeatmap.splitTitle.tooltip"
|
||||
defaultMessage="Split chart aggregation is not yet supported with the new charts library. Please enable the heatmap legacy charts library advanced setting to use split chart aggregation."
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -23,13 +23,23 @@ export const getUiSettingsConfig: () => Record<string, UiSettingsParams<boolean>
|
|||
}
|
||||
),
|
||||
requiresPageReload: true,
|
||||
value: true,
|
||||
value: false,
|
||||
description: i18n.translate(
|
||||
'visTypeHeatmap.advancedSettings.visualization.legacyHeatmapChartsLibrary.description',
|
||||
{
|
||||
defaultMessage: 'Enables legacy charts library for heatmap charts in visualize.',
|
||||
}
|
||||
),
|
||||
deprecation: {
|
||||
message: i18n.translate(
|
||||
'visTypeHeatmap.advancedSettings.visualization.legacyHeatmapChartsLibrary.deprecation',
|
||||
{
|
||||
defaultMessage:
|
||||
'The legacy charts library for heatmap in visualize is deprecated and will not be supported in a future version.',
|
||||
}
|
||||
),
|
||||
docLinksKey: 'visualizationSettings',
|
||||
},
|
||||
category: ['visualization'],
|
||||
schema: schema.boolean(),
|
||||
},
|
||||
|
|
|
@ -120,47 +120,6 @@ describe('VisualizeEditorCommon', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should display a warning callout for new heatmap implementation with split aggs', async () => {
|
||||
const wrapper = shallowWithIntl(
|
||||
<VisualizeEditorCommon
|
||||
appState={null}
|
||||
hasUnsavedChanges={false}
|
||||
setHasUnsavedChanges={() => {}}
|
||||
hasUnappliedChanges={false}
|
||||
isEmbeddableRendered={false}
|
||||
onAppLeave={() => {}}
|
||||
visEditorRef={React.createRef()}
|
||||
visInstance={
|
||||
{
|
||||
savedVis: {
|
||||
id: 'test',
|
||||
sharingSavedObjectProps: {
|
||||
outcome: 'conflict',
|
||||
aliasTargetId: 'alias_id',
|
||||
},
|
||||
},
|
||||
vis: {
|
||||
type: {
|
||||
title: 'Heatmap',
|
||||
name: 'heatmap',
|
||||
},
|
||||
data: {
|
||||
aggs: {
|
||||
aggs: [
|
||||
{
|
||||
schema: 'split',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as unknown as VisualizeEditorVisInstance
|
||||
}
|
||||
/>
|
||||
);
|
||||
expect(wrapper.find(VizChartWarning).length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not display a warning callout for XY charts with split aggs', async () => {
|
||||
const wrapper = shallowWithIntl(
|
||||
<VisualizeEditorCommon
|
||||
|
|
|
@ -98,23 +98,6 @@ const GaugeWarningFormatMessage: FC<WarningMessageProps> = (props) => {
|
|||
);
|
||||
};
|
||||
|
||||
const HeatmapWarningFormatMessage: FC<WarningMessageProps> = (props) => {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="visualizations.newHeatmapChart.notificationMessage"
|
||||
defaultMessage="The new heatmap charts library does not yet support split chart aggregation. {conditionalMessage}"
|
||||
values={{
|
||||
conditionalMessage: (
|
||||
<>
|
||||
<SwitchToOldLibraryMessage {...props} />
|
||||
<ContactAdminMessage {...props} />
|
||||
</>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const PieWarningFormatMessage: FC<WarningMessageProps> = (props) => {
|
||||
return (
|
||||
<FormattedMessage
|
||||
|
@ -142,7 +125,6 @@ const ControlsWarningFormatMessage: FC<WarningMessageProps> = (props) => {
|
|||
};
|
||||
|
||||
const warningMessages = {
|
||||
[CHARTS_WITHOUT_SMALL_MULTIPLES.heatmap]: HeatmapWarningFormatMessage,
|
||||
[CHARTS_WITHOUT_SMALL_MULTIPLES.gauge]: GaugeWarningFormatMessage,
|
||||
[CHARTS_TO_BE_DEPRECATED.pie]: PieWarningFormatMessage,
|
||||
[CHARTS_TO_BE_DEPRECATED.controls]: ControlsWarningFormatMessage,
|
||||
|
|
|
@ -6,5 +6,4 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const NEW_HEATMAP_CHARTS_LIBRARY = 'visualization:visualize:legacyHeatmapChartsLibrary';
|
||||
export const NEW_GAUGE_CHARTS_LIBRARY = 'visualization:visualize:legacyGaugeChartsLibrary';
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
|
||||
import { $Values } from '@kbn/utility-types';
|
||||
import { AggConfigs } from '@kbn/data-plugin/common';
|
||||
import { NEW_HEATMAP_CHARTS_LIBRARY, NEW_GAUGE_CHARTS_LIBRARY } from '../constants';
|
||||
import { NEW_GAUGE_CHARTS_LIBRARY } from '../constants';
|
||||
|
||||
export const CHARTS_WITHOUT_SMALL_MULTIPLES = {
|
||||
heatmap: 'heatmap',
|
||||
gauge: 'gauge',
|
||||
} as const;
|
||||
|
||||
|
@ -24,7 +23,6 @@ export type CHARTS_WITHOUT_SMALL_MULTIPLES = $Values<typeof CHARTS_WITHOUT_SMALL
|
|||
export type CHARTS_TO_BE_DEPRECATED = $Values<typeof CHARTS_TO_BE_DEPRECATED>;
|
||||
|
||||
export const CHARTS_CONFIG_TOKENS = {
|
||||
[CHARTS_WITHOUT_SMALL_MULTIPLES.heatmap]: NEW_HEATMAP_CHARTS_LIBRARY,
|
||||
[CHARTS_WITHOUT_SMALL_MULTIPLES.gauge]: NEW_GAUGE_CHARTS_LIBRARY,
|
||||
} as const;
|
||||
|
||||
|
@ -32,7 +30,6 @@ export const isSplitChart = (chartType: string | undefined, aggs?: AggConfigs) =
|
|||
const defaultIsSplitChart = () => aggs?.aggs.some((agg) => agg.schema === 'split');
|
||||
|
||||
const knownCheckers = {
|
||||
[CHARTS_WITHOUT_SMALL_MULTIPLES.heatmap]: defaultIsSplitChart,
|
||||
[CHARTS_WITHOUT_SMALL_MULTIPLES.gauge]: () => aggs?.aggs.some((agg) => agg.schema === 'group'),
|
||||
};
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
const expectNoDataRenders = async () => {
|
||||
await pieChart.expectEmptyPieChart();
|
||||
await dashboardExpect.seriesElementCount(0);
|
||||
await dashboardExpect.heatMapNoResults();
|
||||
await dashboardExpect.dataTableNoResult();
|
||||
await dashboardExpect.savedSearchNoResult();
|
||||
await dashboardExpect.inputControlItemCount(5);
|
||||
|
|
|
@ -91,7 +91,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('area, bar and heatmap charts filtered', async () => {
|
||||
await dashboardExpect.seriesElementCount(0);
|
||||
await dashboardExpect.heatMapNoResults();
|
||||
});
|
||||
|
||||
it('data tables are filtered', async () => {
|
||||
|
@ -156,7 +156,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('area, bar and heatmap charts filtered', async () => {
|
||||
await dashboardExpect.seriesElementCount(0);
|
||||
await dashboardExpect.heatMapNoResults();
|
||||
});
|
||||
|
||||
it('data tables are filtered', async () => {
|
||||
|
@ -212,7 +212,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
it('area, bar and heatmap charts', async () => {
|
||||
await dashboardExpect.seriesElementCount(2);
|
||||
await dashboardExpect.heatmapXAxisBuckets(11);
|
||||
});
|
||||
|
||||
it('data tables', async () => {
|
||||
|
|
|
@ -21,6 +21,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
|||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
await kibanaServer.uiSettings.update({
|
||||
'histogram:maxBars': 100,
|
||||
'visualization:visualize:legacyHeatmapChartsLibrary': true,
|
||||
});
|
||||
await browser.refresh();
|
||||
|
||||
|
@ -31,6 +32,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
|||
after(async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
'histogram:maxBars': 1000,
|
||||
'visualization:visualize:legacyHeatmapChartsLibrary': false,
|
||||
});
|
||||
await browser.refresh();
|
||||
});
|
||||
|
|
|
@ -27,7 +27,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
|||
|
||||
before(async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
'visualization:visualize:legacyHeatmapChartsLibrary': false,
|
||||
'histogram:maxBars': 100,
|
||||
});
|
||||
await browser.refresh();
|
||||
|
@ -35,7 +34,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
|||
|
||||
after(async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
'visualization:visualize:legacyHeatmapChartsLibrary': true,
|
||||
'histogram:maxBars': 1000,
|
||||
});
|
||||
await browser.refresh();
|
||||
|
|
|
@ -16,6 +16,7 @@ export class DashboardExpectService extends FtrService {
|
|||
private readonly testSubjects = this.ctx.getService('testSubjects');
|
||||
private readonly find = this.ctx.getService('find');
|
||||
private readonly filterBar = this.ctx.getService('filterBar');
|
||||
private readonly elasticChart = this.ctx.getService('elasticChart');
|
||||
|
||||
private readonly dashboard = this.ctx.getPageObject('dashboard');
|
||||
private readonly visChart = this.ctx.getPageObject('visChart');
|
||||
|
@ -304,12 +305,22 @@ export class DashboardExpectService extends FtrService {
|
|||
});
|
||||
}
|
||||
|
||||
// heatmap data
|
||||
async seriesElementCount(expectedCount: number) {
|
||||
this.log.debug(`DashboardExpect.seriesElementCount(${expectedCount})`);
|
||||
await this.retry.try(async () => {
|
||||
const seriesElements = await this.find.allByCssSelector('.series', this.findTimeout);
|
||||
expect(seriesElements.length).to.be(expectedCount);
|
||||
});
|
||||
const heatmapData = await this.elasticChart.getChartDebugData('heatmapChart');
|
||||
this.log.debug(heatmapData.axes?.y[0]);
|
||||
expect(heatmapData.axes?.y[0].labels.length).to.be(expectedCount);
|
||||
}
|
||||
|
||||
async heatmapXAxisBuckets(expectedCount: number) {
|
||||
this.log.debug(`DashboardExpect.heatmapXAxisBuckets(${expectedCount})`);
|
||||
const heatmapData = await this.elasticChart.getChartDebugData('heatmapChart');
|
||||
expect(heatmapData.axes?.x[0].labels.length).to.be(expectedCount);
|
||||
}
|
||||
|
||||
async heatMapNoResults() {
|
||||
await this.testSubjects.find('heatmapChart>emptyPlaceholder');
|
||||
}
|
||||
|
||||
// legacy controls visualization
|
||||
|
|
|
@ -6817,7 +6817,6 @@
|
|||
"visualizations.missedDataView.errorMessage": "Impossible de trouver {type} : {id}",
|
||||
"visualizations.newChart.conditionalMessage.newLibrary": "Passer à la bibliothèque {type} dans {link}",
|
||||
"visualizations.newGaugeChart.notificationMessage": "La nouvelle bibliothèque de graphiques de jauge ne prend pas encore en charge l'agrégation de graphiques fractionnés. {conditionalMessage}",
|
||||
"visualizations.newHeatmapChart.notificationMessage": "La nouvelle bibliothèque de graphiques de cartes thermiques ne prend pas encore en charge l'agrégation de graphiques fractionnés. {conditionalMessage}",
|
||||
"visualizations.newVisWizard.newVisTypeTitle": "Nouveau {visTypeName}",
|
||||
"visualizations.newVisWizard.resultsFound": "{resultCount, plural, one {type trouvé} other {types trouvés}}",
|
||||
"visualizations.noMatchRoute.bannerText": "L'application Visualize ne reconnaît pas cet itinéraire : {route}.",
|
||||
|
@ -37904,7 +37903,6 @@
|
|||
"visTypeHeatmap.scaleTypes.linearText": "Linéaire",
|
||||
"visTypeHeatmap.scaleTypes.logText": "Logarithmique",
|
||||
"visTypeHeatmap.scaleTypes.squareRootText": "Racine carrée",
|
||||
"visTypeHeatmap.splitTitle.tooltip": "l'agrégation de graphique divisé n’est pas encore compatible avec la nouvelle bibliothèque de graphiques. Veuillez activer le paramètre avancé de la bibliothèque de graphiques héritée pour les cartes thermiques afin d'utiliser l'agrégation de graphique divisé.",
|
||||
"visTypeMarkdown.function.font.help": "Paramètres de police.",
|
||||
"visTypeMarkdown.function.help": "Visualisation Markdown",
|
||||
"visTypeMarkdown.function.markdown.help": "Markdown à rendre",
|
||||
|
|
|
@ -6818,7 +6818,6 @@
|
|||
"visualizations.missedDataView.errorMessage": "{type}が見つかりませんでした:{id}",
|
||||
"visualizations.newChart.conditionalMessage.newLibrary": "{link}で{type}ライブラリに切り替える",
|
||||
"visualizations.newGaugeChart.notificationMessage": "新しいゲージグラフライブラリはまだ分割グラフアグリゲーションをサポートしていません。{conditionalMessage}",
|
||||
"visualizations.newHeatmapChart.notificationMessage": "新しいヒートマップグラフライブラリはまだ分割グラフアグリゲーションをサポートしていません。{conditionalMessage}",
|
||||
"visualizations.newVisWizard.newVisTypeTitle": "新規{visTypeName}",
|
||||
"visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {タイプ}}が見つかりました",
|
||||
"visualizations.noMatchRoute.bannerText": "Visualizeアプリケーションはこのルートを認識できません。{route}",
|
||||
|
@ -37872,7 +37871,6 @@
|
|||
"visTypeHeatmap.scaleTypes.linearText": "線形",
|
||||
"visTypeHeatmap.scaleTypes.logText": "ログ",
|
||||
"visTypeHeatmap.scaleTypes.squareRootText": "平方根",
|
||||
"visTypeHeatmap.splitTitle.tooltip": "分割グラフアグリゲーションは、新しいグラフライブラリでまだサポートされていません。分割グラフアグリゲーションを使用するには、ヒートマップのレガシーグラフライブラリと詳細設定を有効にしてください。",
|
||||
"visTypeMarkdown.function.font.help": "フォント設定です。",
|
||||
"visTypeMarkdown.function.help": "マークダウンビジュアライゼーション",
|
||||
"visTypeMarkdown.function.markdown.help": "レンダリングするマークダウン",
|
||||
|
|
|
@ -6818,7 +6818,6 @@
|
|||
"visualizations.missedDataView.errorMessage": "找不到 {type}:{id}",
|
||||
"visualizations.newChart.conditionalMessage.newLibrary": "切换到{link}中的{type}库",
|
||||
"visualizations.newGaugeChart.notificationMessage": "新的仪表盘图表库尚不支持拆分图表聚合。{conditionalMessage}",
|
||||
"visualizations.newHeatmapChart.notificationMessage": "新的热图图表库尚不支持拆分图表聚合。{conditionalMessage}",
|
||||
"visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}",
|
||||
"visualizations.newVisWizard.resultsFound": "{resultCount, plural, other {类型}}已找到",
|
||||
"visualizations.noMatchRoute.bannerText": "Visualize 应用程序无法识别此路由:{route}。",
|
||||
|
@ -37899,7 +37898,6 @@
|
|||
"visTypeHeatmap.scaleTypes.linearText": "线性",
|
||||
"visTypeHeatmap.scaleTypes.logText": "对数",
|
||||
"visTypeHeatmap.scaleTypes.squareRootText": "平方根",
|
||||
"visTypeHeatmap.splitTitle.tooltip": "新图表库尚不支持拆分图表聚合。请启用热图旧版图表库高级设置以使用拆分图表聚合。",
|
||||
"visTypeMarkdown.function.font.help": "字体设置。",
|
||||
"visTypeMarkdown.function.help": "Markdown 可视化",
|
||||
"visTypeMarkdown.function.markdown.help": "要渲染的 Markdown",
|
||||
|
|
|
@ -32,6 +32,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await kibanaServer.importExport.load(
|
||||
`x-pack/test/functional/fixtures/kbn_archiver/dashboard/with_by_value_visualizations`
|
||||
);
|
||||
await kibanaServer.uiSettings.update({
|
||||
'histogram:maxBars': 100,
|
||||
'visualization:visualize:legacyHeatmapChartsLibrary': true,
|
||||
});
|
||||
|
||||
await retry.try(async () => {
|
||||
await PageObjects.common.navigateToApp('dashboard');
|
||||
|
@ -49,6 +53,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/dashboard/with_by_value_visualizations'
|
||||
);
|
||||
await kibanaServer.uiSettings.update({
|
||||
'histogram:maxBars': 1000,
|
||||
'visualization:visualize:legacyHeatmapChartsLibrary': false,
|
||||
});
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue