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:
Stratoula Kalafateli 2023-04-20 10:07:42 +03:00 committed by GitHub
parent 40d730b2ed
commit 544f908fd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 338 additions and 109 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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',
}),

View file

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

View file

@ -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(),
},

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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é nest 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",

View file

@ -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": "レンダリングするマークダウン",

View file

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

View file

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