Skip to content

Commit

Permalink
Make d3 place nicely with object values (elastic#62004)
Browse files Browse the repository at this point in the history
  • Loading branch information
flash1293 committed Apr 2, 2020
1 parent ea53f5f commit 9d60425
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ function DateRangesParamEditor({
</EuiText>
<EuiSpacer size="s" />

{ranges.map(({ from, to, id }) => {
{ranges.map(({ from, to, id }, index) => {
const deleteBtnTitle = i18n.translate(
'visDefaultEditor.controls.dateRanges.removeRangeButtonAriaLabel',
{
Expand Down Expand Up @@ -154,6 +154,7 @@ function DateRangesParamEditor({
placeholder={FROM_PLACEHOLDER}
value={from || ''}
onChange={ev => onChangeRange(id, 'from', ev.target.value)}
data-test-subj={`visEditorDateRange${index}__from`}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
Expand All @@ -168,6 +169,7 @@ function DateRangesParamEditor({
description: 'End of a date range, e.g. From 2018-02-26 *To* 2018-02-28',
}
)}
data-test-subj={`visEditorDateRange${index}__to`}
compressed
fullWidth={true}
isInvalid={areBothEmpty || !validateDateMath(to)}
Expand Down Expand Up @@ -203,7 +205,12 @@ function DateRangesParamEditor({

<EuiSpacer size="s" />
<EuiFlexItem>
<EuiButtonEmpty iconType="plusInCircleFilled" onClick={onAddRange} size="xs">
<EuiButtonEmpty
iconType="plusInCircleFilled"
onClick={onAddRange}
size="xs"
data-test-subj="visEditorAddDateRange"
>
<FormattedMessage
id="visDefaultEditor.controls.dateRanges.addRangeButtonLabel"
defaultMessage="Add range"
Expand Down
36 changes: 33 additions & 3 deletions src/legacy/core_plugins/vis_type_vislib/public/vislib/lib/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ import { orderXValues } from '../components/zero_injection/ordered_x_keys';
import { labels } from '../components/labels/labels';
import { getFormatService } from '../../services';

// X axis and split series values in a data table can sometimes be objects,
// e.g. when working with date ranges. d3 casts all ordinal values to strings
// which is a problem for these objects because they just return `[object Object]`
// and thus all map to the same value.
// This little helper overwrites the toString method of an object and keeps it the
// same otherwise - allowing d3 to correctly work with the values.
class D3MappableObject {
constructor(data) {
for (const key in data) {
if (data.hasOwnProperty(key)) {
this[key] = data[key];
}
}
}

toString() {
return JSON.stringify(this);
}
}

/**
* Provides an API for pulling values off the data
* and calculating values using the data
Expand Down Expand Up @@ -52,9 +72,14 @@ export class Data {
const copyChart = data => {
const newData = {};
Object.keys(data).forEach(key => {
if (key !== 'series') {
newData[key] = data[key];
} else {
if (key === 'xAxisOrderedValues') {
newData[key] = data[key].map(val => {
if (typeof val === 'object') {
return new D3MappableObject(val);
}
return val;
});
} else if (key === 'series') {
newData[key] = data[key].map(seri => {
const converter = getFormatService().deserialize(seri.format);
const zConverter = getFormatService().deserialize(seri.zFormat);
Expand All @@ -67,12 +92,17 @@ export class Data {
const newVal = _.clone(val);
newVal.extraMetrics = val.extraMetrics;
newVal.series = val.series || seri.label;
if (typeof newVal.x === 'object') {
newVal.x = new D3MappableObject(newVal.x);
}
return newVal;
}),
yAxisFormatter: val => converter.convert(val),
zAxisFormatter: val => zConverter.convert(val),
};
});
} else {
newData[key] = data[key];
}
});

Expand Down
19 changes: 19 additions & 0 deletions test/functional/apps/visualize/_vertical_bar_chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ export default function({ getService, getPageObjects }) {
});
});

describe('bar charts range on x axis', () => {
it('should individual bars for each configured range', async function() {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
await PageObjects.visEditor.clickBucket('X-axis');
log.debug('Aggregation = Date Range');
await PageObjects.visEditor.selectAggregation('Date Range');
log.debug('Field = @timestamp');
await PageObjects.visEditor.selectField('@timestamp');
await PageObjects.visEditor.clickAddDateRange();
await PageObjects.visEditor.setDateRangeByIndex('1', 'now-2w/w', 'now-1w/w');
await PageObjects.visEditor.clickGo();
const bottomLabels = await PageObjects.visChart.getXAxisLabels();
expect(bottomLabels.length).to.be(2);
});
});

// FLAKY: https://github.com/elastic/kibana/issues/22322
describe.skip('vertical bar chart flaky part', function() {
const vizName1 = 'Visualization VerticalBarChart';
Expand Down
9 changes: 9 additions & 0 deletions test/functional/page_objects/visualize_editor_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ export function VisualizeEditorPageProvider({ getService, getPageObjects }: FtrP
await radioBtn.click();
}

public async clickAddDateRange() {
await testSubjects.click(`visEditorAddDateRange`);
}

public async setDateRangeByIndex(index: string, from: string, to: string) {
await testSubjects.setValue(`visEditorDateRange${index}__from`, from);
await testSubjects.setValue(`visEditorDateRange${index}__to`, to);
}

/**
* Adds new bucket
* @param bucketName bucket name, like 'X-axis', 'Split rows', 'Split series'
Expand Down

0 comments on commit 9d60425

Please sign in to comment.