Skip to content

Commit

Permalink
fix(axes): fix time scale bug with days crossing a new month (#1565)
Browse files Browse the repository at this point in the history
  • Loading branch information
wyze committed Jun 5, 2021
1 parent 8a99932 commit cc27a85
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 41 deletions.
6 changes: 3 additions & 3 deletions packages/axes/src/components/Grid.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useMemo, memo } from 'react'
import { GridLines } from './GridLines'
import { computeGridLines } from '../compute'
import { AnyScale, AxisValue } from '../types'
import { AnyScale, AxisValue, TicksSpec } from '../types'

export const Grid = memo(
<X extends AxisValue, Y extends AxisValue>({
Expand All @@ -15,9 +15,9 @@ export const Grid = memo(
width: number
height: number
xScale?: AnyScale
xValues?: number | X[]
xValues?: TicksSpec<X>
yScale?: AnyScale
yValues?: number | Y[]
yValues?: TicksSpec<Y>
}) => {
const xLines = useMemo(() => {
if (!xScale) return false
Expand Down
80 changes: 51 additions & 29 deletions packages/axes/src/compute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {
utcMinute,
timeHour,
utcHour,
timeDay,
utcDay,
timeWeek,
utcWeek,
timeSunday,
Expand All @@ -30,6 +28,7 @@ import {
utcMonth,
timeYear,
utcYear,
timeInterval,
} from 'd3-time'
import { timeFormat } from 'd3-time-format'
import { format as d3Format } from 'd3-format'
Expand Down Expand Up @@ -58,6 +57,20 @@ export const centerScale = <Value>(scale: ScaleWithBandwidth) => {
return <T extends Value>(d: T) => (scale(d) ?? 0) + offset
}

const timeDay = timeInterval(
date => date.setHours(0, 0, 0, 0),
(date, step) => date.setDate(date.getDate() + step),
(start, end) => (end.getTime() - start.getTime()) / 864e5,
date => Math.floor(date.getTime() / 864e5)
)

const utcDay = timeInterval(
date => date.setUTCHours(0, 0, 0, 0),
(date, step) => date.setUTCDate(date.getUTCDate() + step),
(start, end) => (end.getTime() - start.getTime()) / 864e5,
date => Math.floor(date.getTime() / 864e5)
)

const timeByType: Record<string, [CountableTimeInterval, CountableTimeInterval]> = {
millisecond: [timeMillisecond, utcMillisecond],
second: [timeSecond, utcSecond],
Expand Down Expand Up @@ -93,6 +106,41 @@ export const getScaleTicks = <Value extends AxisValue>(
return spec
}

if (typeof spec === 'string' && 'useUTC' in scale) {
// time interval
const matches = spec.match(timeIntervalRegexp)

if (matches) {
const [, amount, type] = matches
// UTC is used as it's more predictible
// however local time could be used too
// let's see how it fits users' requirements
const timeType = timeByType[type][scale.useUTC ? 1 : 0]

if (type === 'day') {
const [start, originalStop] = scale.domain()
const stop = new Date(originalStop)

// Set range to include last day in the domain since `interval.range` function is exclusive stop
stop.setDate(stop.getDate() + 1)

return timeType.every(Number(amount ?? 1))?.range(start, stop) ?? []
}

if (amount === undefined) {
return scale.ticks(timeType)
}

const interval = timeType.every(Number(amount))

if (interval) {
return scale.ticks(interval)
}
}

throw new Error(`Invalid tickValues: ${spec}`)
}

// continuous scales
if ('ticks' in scale) {
// default behaviour
Expand All @@ -104,29 +152,6 @@ export const getScaleTicks = <Value extends AxisValue>(
if (isInteger(spec)) {
return scale.ticks(spec)
}

if (typeof spec === 'string' && 'useUTC' in scale) {
// time interval
const matches = spec.match(timeIntervalRegexp)
if (matches) {
// UTC is used as it's more predictible
// however local time could be used too
// let's see how it fits users' requirements
const timeType = timeByType[matches[2]][scale.useUTC ? 1 : 0]

if (matches[1] === undefined) {
return scale.ticks(timeType)
}

const interval = timeType.every(Number(matches[1]))

if (interval) {
return scale.ticks(interval)
}
}

throw new Error(`Invalid tickValues: ${spec}`)
}
}

// non linear scale default
Expand Down Expand Up @@ -249,10 +274,7 @@ export const computeGridLines = <Value extends AxisValue>({
values?: TicksSpec<Value>
}) => {
const lineValues = isArray<number>(_values) ? _values : undefined
const lineCount = isInteger(_values) ? _values : undefined

const values = lineValues || getScaleTicks(scale, lineCount)

const values = lineValues || getScaleTicks(scale, _values)
const position = 'bandwidth' in scale ? centerScale(scale) : scale

const lines: Line[] =
Expand Down
2 changes: 1 addition & 1 deletion packages/axes/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export type ScaleWithBandwidth =
export type AnyScale =
| (ScaleLinear<any, number> & { type: 'linear' })
| (ScaleOrdinal<any, number> & { type: 'ordinal' })
| (ScaleTime<any, number> & { useUTC: boolean; type: 'time' })
| (ScaleTime<any, number> & { format: string; useUTC: boolean; type: 'time' })
| (ScaleSymLog<any, number> & { type: 'symlog' })
| (ScaleLogarithmic<any, number> & { type: 'log' })
| ScaleWithBandwidth
Expand Down
6 changes: 3 additions & 3 deletions packages/line/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { OrdinalColorScaleConfig } from '@nivo/colors'
import { LegendProps } from '@nivo/legends'
import { Scale, ScaleFunc } from '@nivo/scales'
import { AxisProps, GridValues } from '@nivo/axes'
import { AxisProps, TicksSpec } from '@nivo/axes'
import { CrosshairType } from '@nivo/tooltip'
import { Line as D3Line } from 'd3-shape'

Expand Down Expand Up @@ -164,9 +164,9 @@ declare module '@nivo/line' {
axisLeft?: AxisProps | null

enableGridX?: boolean
gridXValues?: GridValues<DatumValue>
gridXValues?: TicksSpec<DatumValue>
enableGridY?: boolean
gridYValues?: GridValues<DatumValue>
gridYValues?: TicksSpec<DatumValue>

enablePoints?: boolean
pointSymbol?: (props: Readonly<PointSymbolProps>) => React.ReactNode
Expand Down
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17231,11 +17231,6 @@ lodash.hasin@4.5.2:
resolved "https://registry.yarnpkg.com/lodash.hasin/-/lodash.hasin-4.5.2.tgz#f91e352378d21ef7090b9e7687c2ca35c5b4d52a"
integrity sha1-+R41I3jSHvcJC552h8LKNcW01So=

lodash.isdate@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.isdate/-/lodash.isdate-4.0.1.tgz#35a543673b9d76110de4114b32cc577048a7f366"
integrity sha1-NaVDZzuddhEN5BFLMsxXcEin82Y=

lodash.isempty@4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
Expand Down

0 comments on commit cc27a85

Please sign in to comment.