Skip to content

Commit

Permalink
fix(xy): handle zero-length time domains and switch to 24hr time (#1464)
Browse files Browse the repository at this point in the history
  • Loading branch information
monfera authored Nov 8, 2021
1 parent 0c38291 commit 379c2d6
Show file tree
Hide file tree
Showing 16 changed files with 61 additions and 25 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions integration/tests/axis_stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ describe('Axis stories', () => {
'http://localhost:9001/?path=/story/area-chart--timeslip&globals=theme:light&knob-Bin width in ms (0: none specifed)=0&knob-Minor grid lines=true&knob-Shift time=8.5&knob-Shorter X axis minor whiskers=true&knob-Stretch time=6.8&knob-Time zoom=120&knob-X axis minor whiskers=true&knob-fallback placement=left-start&knob-layerCount=3&knob-placement=left&knob-placement offset=5&knob-showOverlappingLabels time axis=true&knob-showOverlappingTicks time axis=true&knob-stickTo=MousePosition&knob-Horizontal axis title=&knob-Top X axis=true',
);
});
it('renders multilayer time axis with a single point and an arbitrary non-degenerate domain', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/bar-chart--test-single-histogram-bar-chart&globals=theme:light&knob-non-round time domain start=true&knob-use multilayer time axis=true&knob-use lines instead of bars=true',
);
});
it('renders multilayer time axis with a single point and a degenerate, zero width domain', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/bar-chart--test-single-histogram-bar-chart&globals=theme:light&knob-non-round time domain start=&knob-use multilayer time axis=true&knob-use lines instead of bars=true',
);
});
it('should render proper tick count', async () => {
await common.expectChartAtUrlToMatchScreenshot(
'http://localhost:9001/?path=/story/axes--basic&knob-Tick Label Padding=0&knob-debug=&knob-Bottom overlap labels=&knob-Bottom overlap ticks=true&knob-Number of ticks on bottom=20&knob-Left overlap labels=&knob-Left overlap ticks=true&knob-Number of ticks on left=10',
Expand Down
35 changes: 20 additions & 15 deletions packages/charts/src/chart_types/xy_chart/axes/timeslip/rasters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ interface YearToHour extends YearToDay {
hour: number;
}

// todo DRY up the config for the other time units too, where sensible
const hourFormat: Partial<ConstructorParameters<typeof Intl.DateTimeFormat>[1]> = {
hour: '2-digit',
hour12: false,
};

/** @internal */
export const rasters = ({ minimumTickPixelDistance, locale }: RasterConfig, timeZone: string) => {
const minorDayFormat = new Intl.DateTimeFormat(locale, { day: 'numeric', timeZone }).format;
Expand All @@ -89,6 +95,14 @@ export const rasters = ({ minimumTickPixelDistance, locale }: RasterConfig, time
day: 'numeric',
timeZone,
}).format;
const detailedHourFormatBase = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
...hourFormat,
timeZone,
}).format;
const detailedHourFormat = (d: number) => `${detailedHourFormatBase(d)}h`;

const years: TimeRaster<TimeBin & { year: number }> = {
unit: 'year',
Expand Down Expand Up @@ -280,15 +294,9 @@ export const rasters = ({ minimumTickPixelDistance, locale }: RasterConfig, time
labeled: true,
minimumTickPixelDistance: 2 * minimumTickPixelDistance,
binStarts: millisecondBinStarts(60 * 60 * 1000),
detailedLabelFormat: new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
timeZone,
}).format,
detailedLabelFormat: detailedHourFormat,
minorTickLabelFormat: new Intl.DateTimeFormat(locale, {
hour: 'numeric',
...hourFormat,
timeZone,
}).format,
minimumPixelsPerSecond: NaN,
Expand Down Expand Up @@ -333,13 +341,10 @@ export const rasters = ({ minimumTickPixelDistance, locale }: RasterConfig, time
Object.assign(b, { nextTimePointSec: i === a.length - 1 ? b.nextTimePointSec : a[i + 1].timePointSec }),
),
minorTickLabelFormat: new Intl.DateTimeFormat(locale, {
hour: 'numeric',
timeZone,
}).format,
detailedLabelFormat: new Intl.DateTimeFormat(locale, {
hour: 'numeric',
...hourFormat,
timeZone,
}).format,
detailedLabelFormat: detailedHourFormat,
minimumPixelsPerSecond: NaN,
approxWidthInMs: NaN,
};
Expand All @@ -358,7 +363,7 @@ export const rasters = ({ minimumTickPixelDistance, locale }: RasterConfig, time
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
...hourFormat,
minute: 'numeric',
timeZone,
}).format,
Expand Down Expand Up @@ -409,7 +414,7 @@ export const rasters = ({ minimumTickPixelDistance, locale }: RasterConfig, time
year: 'numeric',
month: 'short',
day: 'numeric',
hour: 'numeric',
...hourFormat,
minute: 'numeric',
second: 'numeric',
timeZone,
Expand Down
2 changes: 1 addition & 1 deletion packages/charts/src/chart_types/xy_chart/domains/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type XDomain = Pick<LogScaleOptions, 'logBase'> & {
nice: boolean;
/* if the scale needs to be a band scale: used when displaying bars */
isBandScale: boolean;
/* the minimum interval of the scale if not-ordinal band-scale */
/* the minimum interval of the scale (for time, in milliseconds) if not-ordinal band-scale */
minInterval: number;
/** the configured timezone in the specs or the fallback to the browser local timezone */
timeZone: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,11 +392,12 @@ function getVisibleTickSets(
(combinedEntry: { ticks: AxisTick[] }, l: TimeRaster<TimeBin>, detailedLayerIndex) => {
if (l.labeled) layerIndex++; // we want three (or however many) _labeled_ axis layers; others are useful for minor ticks/gridlines, and for giving coarser structure eg. stronger gridline for every 6th hour of the day
if (layerIndex >= timeAxisLayerCount) return combinedEntry;
const binWidthS = binWidth / 1000;
const { entry } = fillLayerTimeslip(
layerIndex,
detailedLayerIndex,
[...l.binStarts(domainFromS, domainToS)]
.filter((b) => b.nextTimePointSec > domainFromS && b.timePointSec < domainToS)
[...l.binStarts(domainFromS - binWidthS, domainToS + binWidthS)]
.filter((b) => b.nextTimePointSec > domainFromS && b.timePointSec <= domainToS)
.map((b) => 1000 * b.timePointSec),
!l.labeled
? () => ''
Expand Down
34 changes: 27 additions & 7 deletions storybook/stories/bar/44_test_single_histogram.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
* Side Public License, v 1.
*/

import { boolean } from '@storybook/addon-knobs';
import React from 'react';

import {
Axis,
Chart,
HistogramBarSeries,
LineSeries,
niceTimeFormatByDay,
Position,
ScaleType,
Expand All @@ -24,28 +26,46 @@ import { useBaseTheme } from '../../use_base_theme';

// for testing purposes only
export const Example = () => {
const useLine = boolean('use lines instead of bars', false);
const multiLayerAxis = boolean('use multilayer time axis', false);
const oddDomain = boolean('non-round time domain start', false);

const formatter = timeFormatter(niceTimeFormatByDay(1));

const binWidth = 60000;

const xDomain = {
min: NaN,
max: NaN,
minInterval: 60000,
min: KIBANA_METRICS.metrics.kibana_os_load[0].data[0][0] - (oddDomain ? 217839 : 0),
max: KIBANA_METRICS.metrics.kibana_os_load[0].data[0][0] + (oddDomain ? 200000 : 0 ?? binWidth - 1),
minInterval: binWidth,
};

const Series = useLine ? LineSeries : HistogramBarSeries;

return (
<Chart>
<Settings xDomain={xDomain} baseTheme={useBaseTheme()} />
<Axis
id="bottom"
title="timestamp per 1 minute"
position={Position.Bottom}
showOverlappingTicks
showOverlappingTicks={!multiLayerAxis}
tickFormat={formatter}
timeAxisLayerCount={multiLayerAxis ? 3 : 0}
style={
multiLayerAxis
? {
tickLine: { size: 0.0001, padding: 4 },
tickLabel: { alignment: { horizontal: Position.Left, vertical: Position.Bottom } },
}
: {}
}
/>
<Axis id="left" title={KIBANA_METRICS.metrics.kibana_os_load[0].metric.title} position={Position.Left} />
<HistogramBarSeries
id="bars"
xScaleType={ScaleType.Linear}
<Series
id="series"
timeZone="local"
xScaleType={multiLayerAxis ? ScaleType.Time : ScaleType.Linear}
yScaleType={ScaleType.Linear}
xAccessor={0}
yAccessors={[1]}
Expand Down

0 comments on commit 379c2d6

Please sign in to comment.