This commit is contained in:
parent
d41cfa0259
commit
c0ef9d531c
8 changed files with 232 additions and 0 deletions
|
@ -38,6 +38,15 @@ import {
|
|||
} from '../../../../../src/plugins/charts/public';
|
||||
import { LensIconChartDonut } from '../assets/chart_donut';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
/**
|
||||
* Flag used to enable debugState on elastic charts
|
||||
*/
|
||||
_echDebugStateFlag?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
const EMPTY_SLICE = Symbol('empty_slice');
|
||||
|
||||
export function PieComponent(
|
||||
|
@ -251,6 +260,7 @@ export function PieComponent(
|
|||
>
|
||||
<Chart>
|
||||
<Settings
|
||||
debugState={window._echDebugStateFlag ?? false}
|
||||
// Legend is hidden in many scenarios
|
||||
// - Tiny preview
|
||||
// - Treemap does not need a legend because it uses category labels
|
||||
|
|
|
@ -6,6 +6,7 @@ exports[`xy_expression XYChart component it renders area 1`] = `
|
|||
>
|
||||
<Connect(SpecInstance)
|
||||
baseTheme={Object {}}
|
||||
debugState={false}
|
||||
legendPosition="top"
|
||||
onBrushEnd={[Function]}
|
||||
onElementClick={[Function]}
|
||||
|
@ -201,6 +202,7 @@ exports[`xy_expression XYChart component it renders bar 1`] = `
|
|||
>
|
||||
<Connect(SpecInstance)
|
||||
baseTheme={Object {}}
|
||||
debugState={false}
|
||||
legendPosition="top"
|
||||
onBrushEnd={[Function]}
|
||||
onElementClick={[Function]}
|
||||
|
@ -404,6 +406,7 @@ exports[`xy_expression XYChart component it renders horizontal bar 1`] = `
|
|||
>
|
||||
<Connect(SpecInstance)
|
||||
baseTheme={Object {}}
|
||||
debugState={false}
|
||||
legendPosition="top"
|
||||
onBrushEnd={[Function]}
|
||||
onElementClick={[Function]}
|
||||
|
@ -607,6 +610,7 @@ exports[`xy_expression XYChart component it renders line 1`] = `
|
|||
>
|
||||
<Connect(SpecInstance)
|
||||
baseTheme={Object {}}
|
||||
debugState={false}
|
||||
legendPosition="top"
|
||||
onBrushEnd={[Function]}
|
||||
onElementClick={[Function]}
|
||||
|
@ -802,6 +806,7 @@ exports[`xy_expression XYChart component it renders stacked area 1`] = `
|
|||
>
|
||||
<Connect(SpecInstance)
|
||||
baseTheme={Object {}}
|
||||
debugState={false}
|
||||
legendPosition="top"
|
||||
onBrushEnd={[Function]}
|
||||
onElementClick={[Function]}
|
||||
|
@ -1005,6 +1010,7 @@ exports[`xy_expression XYChart component it renders stacked bar 1`] = `
|
|||
>
|
||||
<Connect(SpecInstance)
|
||||
baseTheme={Object {}}
|
||||
debugState={false}
|
||||
legendPosition="top"
|
||||
onBrushEnd={[Function]}
|
||||
onElementClick={[Function]}
|
||||
|
@ -1216,6 +1222,7 @@ exports[`xy_expression XYChart component it renders stacked horizontal bar 1`] =
|
|||
>
|
||||
<Connect(SpecInstance)
|
||||
baseTheme={Object {}}
|
||||
debugState={false}
|
||||
legendPosition="top"
|
||||
onBrushEnd={[Function]}
|
||||
onElementClick={[Function]}
|
||||
|
|
|
@ -60,6 +60,15 @@ import { fittingFunctionDefinitions, getFitOptions } from './fitting_functions';
|
|||
import { getAxesConfiguration } from './axes_configuration';
|
||||
import { getColorAssignments } from './color_assignment';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
/**
|
||||
* Flag used to enable debugState on elastic charts
|
||||
*/
|
||||
_echDebugStateFlag?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
type InferPropType<T> = T extends React.FunctionComponent<infer P> ? P : T;
|
||||
type SeriesSpec = InferPropType<typeof LineSeries> &
|
||||
InferPropType<typeof BarSeries> &
|
||||
|
@ -508,6 +517,7 @@ export function XYChart({
|
|||
return (
|
||||
<Chart>
|
||||
<Settings
|
||||
debugState={window._echDebugStateFlag ?? false}
|
||||
showLegend={
|
||||
legend.isVisible && !legend.showSingleSeries
|
||||
? chartHasMoreThanOneSeries
|
||||
|
|
|
@ -94,6 +94,7 @@ const valueLabelsOptions: Array<{
|
|||
id: string;
|
||||
value: 'hide' | 'inside' | 'outside';
|
||||
label: string;
|
||||
'data-test-subj': string;
|
||||
}> = [
|
||||
{
|
||||
id: `value_labels_hide`,
|
||||
|
@ -101,6 +102,7 @@ const valueLabelsOptions: Array<{
|
|||
label: i18n.translate('xpack.lens.xyChart.valueLabelsVisibility.auto', {
|
||||
defaultMessage: 'Hide',
|
||||
}),
|
||||
'data-test-subj': 'lnsXY_valueLabels_hide',
|
||||
},
|
||||
{
|
||||
id: `value_labels_inside`,
|
||||
|
@ -108,6 +110,7 @@ const valueLabelsOptions: Array<{
|
|||
label: i18n.translate('xpack.lens.xyChart.valueLabelsVisibility.inside', {
|
||||
defaultMessage: 'Show',
|
||||
}),
|
||||
'data-test-subj': 'lnsXY_valueLabels_inside',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -513,6 +516,7 @@ export function DimensionEditor(
|
|||
legend={i18n.translate('xpack.lens.xyChart.axisSide.label', {
|
||||
defaultMessage: 'Axis side',
|
||||
})}
|
||||
data-test-subj="lnsXY_axisSide_groups"
|
||||
name="axisSide"
|
||||
buttonSize="compressed"
|
||||
options={[
|
||||
|
@ -521,6 +525,7 @@ export function DimensionEditor(
|
|||
label: i18n.translate('xpack.lens.xyChart.axisSide.auto', {
|
||||
defaultMessage: 'Auto',
|
||||
}),
|
||||
'data-test-subj': 'lnsXY_axisSide_groups_auto',
|
||||
},
|
||||
{
|
||||
id: `${idPrefix}left`,
|
||||
|
@ -531,6 +536,7 @@ export function DimensionEditor(
|
|||
: i18n.translate('xpack.lens.xyChart.axisSide.left', {
|
||||
defaultMessage: 'Left',
|
||||
}),
|
||||
'data-test-subj': 'lnsXY_axisSide_groups_left',
|
||||
},
|
||||
{
|
||||
id: `${idPrefix}right`,
|
||||
|
@ -541,6 +547,7 @@ export function DimensionEditor(
|
|||
: i18n.translate('xpack.lens.xyChart.axisSide.right', {
|
||||
defaultMessage: 'Right',
|
||||
}),
|
||||
'data-test-subj': 'lnsXY_axisSide_groups_right',
|
||||
},
|
||||
]}
|
||||
idSelected={`${idPrefix}${axisMode}`}
|
||||
|
|
107
x-pack/test/functional/apps/lens/chart_data.ts
Normal file
107
x-pack/test/functional/apps/lens/chart_data.ts
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* 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 { DebugState } from '@elastic/charts';
|
||||
import expect from '@kbn/expect';
|
||||
import { range } from 'lodash';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const PageObjects = getPageObjects(['visualize', 'lens', 'common', 'header']);
|
||||
const elasticChart = getService('elasticChart');
|
||||
|
||||
describe('lens chart data', () => {
|
||||
before(async () => {
|
||||
await PageObjects.visualize.navigateToNewVisualization();
|
||||
await PageObjects.visualize.clickVisType('lens');
|
||||
await elasticChart.setNewChartUiDebugFlag(true);
|
||||
await PageObjects.lens.goToTimeRange();
|
||||
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'terms',
|
||||
field: 'ip',
|
||||
});
|
||||
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'avg',
|
||||
field: 'bytes',
|
||||
});
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
});
|
||||
|
||||
const expectedData = [
|
||||
{ x: '0.53.251.53', y: 4624.75 },
|
||||
{ x: '0.108.3.2', y: 7359.41 },
|
||||
{ x: '0.209.80.244', y: 6169.9 },
|
||||
{ x: '0.228.1.71', y: 7092.8 },
|
||||
{ x: '0.254.91.215', y: 3835.58 },
|
||||
{ x: '__other__', y: 5727.24 },
|
||||
];
|
||||
|
||||
function assertMatchesExpectedData(state: DebugState) {
|
||||
expect(
|
||||
state.bars![0].bars.map((bar) => ({
|
||||
x: bar.x,
|
||||
y: Math.round(bar.y * 100) / 100,
|
||||
}))
|
||||
).to.eql(expectedData);
|
||||
}
|
||||
|
||||
it('should render xy chart', async () => {
|
||||
const data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
assertMatchesExpectedData(data!);
|
||||
});
|
||||
|
||||
// Partition chart tests have to be skipped until
|
||||
// https://github.com/elastic/elastic-charts/issues/917 gets fixed
|
||||
it.skip('should render pie chart', async () => {
|
||||
await PageObjects.lens.switchToVisualization('pie');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
const data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
assertMatchesExpectedData(data!);
|
||||
});
|
||||
|
||||
it.skip('should render donut chart', async () => {
|
||||
await PageObjects.lens.switchToVisualization('donut');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
const data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
assertMatchesExpectedData(data!);
|
||||
});
|
||||
|
||||
it.skip('should render treemap chart', async () => {
|
||||
await PageObjects.lens.switchToVisualization('treemap');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
const data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
assertMatchesExpectedData(data!);
|
||||
});
|
||||
|
||||
it('should render datatable', async () => {
|
||||
await PageObjects.lens.switchToVisualization('lnsDatatable');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
const terms = await Promise.all(
|
||||
range(0, 6).map((index) => PageObjects.lens.getDatatableCellText(index, 0))
|
||||
);
|
||||
const values = await Promise.all(
|
||||
range(0, 6).map((index) => PageObjects.lens.getDatatableCellText(index, 1))
|
||||
);
|
||||
expect(terms.map((term) => (term === 'Other' ? '__other__' : term))).to.eql(
|
||||
expectedData.map(({ x }) => x)
|
||||
);
|
||||
expect(values.map((value) => Math.round(100 * Number(value.replace(',', ''))) / 100)).to.eql(
|
||||
expectedData.map(({ y }) => y)
|
||||
);
|
||||
});
|
||||
|
||||
it('should render metric', async () => {
|
||||
await PageObjects.lens.switchToVisualization('lnsMetric');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
await PageObjects.lens.assertMetric('Average of bytes', '5,727.322');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -31,6 +31,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./dashboard'));
|
||||
loadTestFile(require.resolve('./persistent_context'));
|
||||
loadTestFile(require.resolve('./colors'));
|
||||
loadTestFile(require.resolve('./chart_data'));
|
||||
loadTestFile(require.resolve('./drag_and_drop'));
|
||||
loadTestFile(require.resolve('./lens_reporting'));
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const find = getService('find');
|
||||
const listingTable = getService('listingTable');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const elasticChart = getService('elasticChart');
|
||||
|
||||
describe('lens smokescreen tests', () => {
|
||||
it('should allow creation of lens xy chart', async () => {
|
||||
|
@ -191,6 +192,82 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await testSubjects.missingOrFail('lnsXY_yDimensionPanel > lns-dimensionTrigger');
|
||||
});
|
||||
|
||||
it('should allow creation of a multi-axis chart', async () => {
|
||||
await PageObjects.visualize.navigateToNewVisualization();
|
||||
await PageObjects.visualize.clickVisType('lens');
|
||||
await elasticChart.setNewChartUiDebugFlag(true);
|
||||
await PageObjects.lens.goToTimeRange();
|
||||
await PageObjects.lens.switchToVisualization('bar');
|
||||
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsXY_xDimensionPanel > lns-empty-dimension',
|
||||
operation: 'terms',
|
||||
field: 'geo.dest',
|
||||
});
|
||||
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'avg',
|
||||
field: 'bytes',
|
||||
});
|
||||
|
||||
await PageObjects.lens.configureDimension({
|
||||
dimension: 'lnsXY_yDimensionPanel > lns-empty-dimension',
|
||||
operation: 'cardinality',
|
||||
field: 'bytes',
|
||||
keepOpen: true,
|
||||
});
|
||||
|
||||
await PageObjects.lens.changeAxisSide('right');
|
||||
|
||||
await PageObjects.lens.closeDimensionEditor();
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
const data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
expect(data?.axes?.y.length).to.eql(2);
|
||||
expect(data?.axes?.y.some(({ position }) => position === 'right')).to.eql(true);
|
||||
});
|
||||
|
||||
it('should show value labels on bar charts when enabled', async () => {
|
||||
// enable value labels
|
||||
await PageObjects.lens.toggleToolbarPopover('lnsValuesButton');
|
||||
await testSubjects.click('lnsXY_valueLabels_inside');
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
// check for value labels
|
||||
let data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
expect(data?.bars?.[0].labels).not.to.eql(0);
|
||||
|
||||
// switch to stacked bar chart
|
||||
await PageObjects.lens.switchToVisualization('bar_stacked');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
// check for value labels
|
||||
data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
expect(data?.bars?.[0].labels.length).to.eql(0);
|
||||
});
|
||||
|
||||
it('should override axis title', async () => {
|
||||
const axisTitle = 'overridden axis';
|
||||
await PageObjects.lens.toggleToolbarPopover('lnsLeftAxisButton');
|
||||
await testSubjects.setValue('lnsyLeftAxisTitle', axisTitle, {
|
||||
clearWithKeyboard: true,
|
||||
});
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
let data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
expect(data?.axes?.y?.[0].title).to.eql(axisTitle);
|
||||
|
||||
// hide the gridlines
|
||||
await testSubjects.click('lnsshowyLeftAxisGridlines');
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
data = await PageObjects.lens.getCurrentChartDebugState();
|
||||
expect(data?.axes?.y?.[0].gridlines.length).to.eql(0);
|
||||
});
|
||||
|
||||
it('should transition from a multi-layer stacked bar to donut chart using suggestions', async () => {
|
||||
await PageObjects.visualize.navigateToNewVisualization();
|
||||
await PageObjects.visualize.clickVisType('lens');
|
||||
|
|
|
@ -12,6 +12,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
const log = getService('log');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const elasticChart = getService('elasticChart');
|
||||
const find = getService('find');
|
||||
const comboBox = getService('comboBox');
|
||||
const browser = getService('browser');
|
||||
|
@ -216,6 +217,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
});
|
||||
},
|
||||
|
||||
async toggleToolbarPopover(buttonTestSub: string) {
|
||||
await testSubjects.click(buttonTestSub);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the specified dimension.
|
||||
*
|
||||
|
@ -353,6 +358,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
});
|
||||
},
|
||||
|
||||
async changeAxisSide(newSide: string) {
|
||||
await testSubjects.click(`lnsXY_axisSide_groups_${newSide}`);
|
||||
},
|
||||
|
||||
/** Counts the visible warnings in the config panel */
|
||||
async getErrorCount() {
|
||||
const moreButton = await testSubjects.exists('configuration-failure-more-errors');
|
||||
|
@ -484,6 +493,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
|
|||
);
|
||||
},
|
||||
|
||||
async getCurrentChartDebugState() {
|
||||
return await elasticChart.getChartDebugData('lnsWorkspace');
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets text of the specified datatable header cell
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue