[ui/formatters] Implement number field formatter for durations. Fixes #5130

This commit is contained in:
Nicolas Bevacqua 2016-03-10 13:36:43 -08:00
parent 9378729a60
commit 19bf209c95
7 changed files with 190 additions and 0 deletions

View file

@ -11,6 +11,7 @@ var config;
var formatIds = [
'bytes',
'date',
'duration',
'ip',
'number',
'percent',

View file

@ -0,0 +1,50 @@
import expect from 'expect.js';
import ngMock from 'ngMock';
import RegistryFieldFormatsProvider from 'ui/registry/field_formats';
describe('Duration Format', function () {
let fieldFormats;
let DurationFormat;
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function (Private) {
fieldFormats = Private(RegistryFieldFormatsProvider);
DurationFormat = fieldFormats.getType('duration');
}));
test({ inputFormat: 'seconds', outputFormat: 'humanize' })
(-60, 'minus a minute')
(60, 'a minute')
(125, '2 minutes');
test({ inputFormat: 'minutes', outputFormat: 'humanize' })
(-60, 'minus an hour')
(60, 'an hour')
(125, '2 hours');
test({ inputFormat: 'minutes', outputFormat: 'asHours' }) // outputPrecision defaults to: 2
(-60, '-1.00')
(60, '1.00')
(125, '2.08');
test({ inputFormat: 'seconds', outputFormat: 'asSeconds', outputPrecision: 0 })
(-60, '-60')
(60, '60')
(125, '125');
test({ inputFormat: 'seconds', outputFormat: 'asSeconds', outputPrecision: 2 })
(-60, '-60.00')
(-32.333, '-32.33')
(60, '60.00')
(125, '125.00');
function test({ inputFormat, outputFormat, outputPrecision }) {
return function testFixture(input, output) {
it(`should format ${input} ${inputFormat} through ${outputFormat}${outputPrecision ? `, ${outputPrecision} decimals` : ''}`, () => {
const duration = new DurationFormat({ inputFormat, outputFormat, outputPrecision });
expect(duration.convert(input)).to.eql(output);
});
return testFixture;
};
}
});

View file

@ -5,6 +5,7 @@ import './_string';
import './_url';
import './_color';
import './_date';
import './_duration';
import './_truncate';
describe('Stringify Component', function () {
});

View file

@ -0,0 +1,28 @@
<div class="editor-duration">
<div class="form-group">
<label>Input Format</label>
<select
ng-model="editor.formatParams.inputFormat"
ng-options="inputFormat.kind as inputFormat.text for inputFormat in editor.field.format.type.inputFormats"
class="form-control">
</select>
</div>
<div class="form-group">
<label>Output Format</label>
<select
ng-model="editor.formatParams.outputFormat"
ng-options="outputFormat.method as outputFormat.text for outputFormat in editor.field.format.type.outputFormats"
class="form-control">
</select>
</div>
<div class="form-group" ng-hide="editor.field.format.isHuman()">
<label>Decimal Places</label>
<input type="number" min="0" max="20" ng-model="editor.formatParams.outputPrecision" class="form-control" />
</div>
</div>
<div>
<field-format-editor-samples
ng-model="editor.formatParams"
inputs="cntrl.sampleInputs">
</field-format-editor-samples>
</div>

View file

@ -0,0 +1,10 @@
.editor-duration {
display: flex;
> .form-group {
flex: 1 1 1%;
padding-right: 5px;
&:last-child {
padding-right: 0;
}
}
}

View file

@ -2,6 +2,7 @@ import fieldFormats from 'ui/registry/field_formats';
import stringifyUrl from 'ui/stringify/types/Url';
import stringifyBytes from 'ui/stringify/types/Bytes';
import stringifyDate from 'ui/stringify/types/Date';
import stringifyDuration from 'ui/stringify/types/duration';
import stringifyIp from 'ui/stringify/types/Ip';
import stringifyNumber from 'ui/stringify/types/Number';
import stringifyPercent from 'ui/stringify/types/Percent';
@ -12,6 +13,7 @@ import stringifyTruncate from 'ui/stringify/types/truncate';
fieldFormats.register(stringifyUrl);
fieldFormats.register(stringifyBytes);
fieldFormats.register(stringifyDate);
fieldFormats.register(stringifyDuration);
fieldFormats.register(stringifyIp);
fieldFormats.register(stringifyNumber);
fieldFormats.register(stringifyPercent);

View file

@ -0,0 +1,98 @@
import 'ui/stringify/editors/duration.less';
import _ from 'lodash';
import moment from 'moment';
import IndexPatternsFieldFormatProvider from 'ui/index_patterns/_field_format/FieldFormat';
import durationTemplate from 'ui/stringify/editors/duration.html';
export default function DurationFormatProvider(Private) {
const ratioToSeconds = {
picoseconds: 0.000000000001,
nanoseconds: 0.000000001,
microseconds: 0.000001
};
const FieldFormat = Private(IndexPatternsFieldFormatProvider);
const HUMAN_FRIENDLY = 'humanize';
const DEFAULT_OUTPUT_PRECISION = 2;
const DEFAULT_INPUT_FORMAT = { text: 'Seconds', kind: 'seconds' };
const inputFormats = [
{ text: 'Picoseconds', kind: 'picoseconds' },
{ text: 'Nanoseconds', kind: 'nanoseconds' },
{ text: 'Microseconds', kind: 'microseconds' },
{ text: 'Milliseconds', kind: 'milliseconds' },
DEFAULT_INPUT_FORMAT,
{ text: 'Minutes', kind: 'minutes' },
{ text: 'Hours', kind: 'hours' },
{ text: 'Days', kind: 'days' },
{ text: 'Weeks', kind: 'weeks' },
{ text: 'Months', kind: 'months' },
{ text: 'Years', kind: 'years' }
];
const DEFAULT_OUTPUT_FORMAT = { text: 'Human Readable', method: 'humanize' };
const outputFormats = [
DEFAULT_OUTPUT_FORMAT,
{ text: 'Milliseconds', method: 'asMilliseconds' },
{ text: 'Seconds', method: 'asSeconds' },
{ text: 'Minutes', method: 'asMinutes' },
{ text: 'Hours', method: 'asHours' },
{ text: 'Days', method: 'asDays' },
{ text: 'Weeks', method: 'asWeeks' },
{ text: 'Months', method: 'asMonths' },
{ text: 'Years', method: 'asYears' }
];
class Duration extends FieldFormat {
isHuman() {
return this.param('outputFormat') === HUMAN_FRIENDLY;
}
_convert(val) {
const inputFormat = this.param('inputFormat');
const outputFormat = this.param('outputFormat');
const outputPrecision = this.param('outputPrecision');
const human = this.isHuman();
const prefix = val < 0 && human ? 'minus ' : '';
const duration = parseInputAsDuration(val, inputFormat);
const formatted = duration[outputFormat]();
const precise = human ? formatted : formatted.toFixed(outputPrecision);
return prefix + precise;
}
}
Duration.id = 'duration';
Duration.title = 'Duration';
Duration.fieldType = 'number';
Duration.inputFormats = inputFormats;
Duration.outputFormats = outputFormats;
Duration.editor = {
template: durationTemplate,
controllerAs: 'cntrl',
controller($scope, $interval) {
this.sampleInputs = [
-123,
1,
12,
123,
658,
1988,
3857,
123292,
923528271
];
}
};
Duration.paramDefaults = {
inputFormat: DEFAULT_INPUT_FORMAT.kind,
outputFormat: DEFAULT_OUTPUT_FORMAT.method,
outputPrecision: DEFAULT_OUTPUT_PRECISION
};
return Duration;
function parseInputAsDuration(val, inputFormat) {
const ratio = ratioToSeconds[inputFormat] || 1;
const kind = inputFormat in ratioToSeconds ? 'seconds' : inputFormat;
return moment.duration(val * ratio, kind);
}
};