Skip to content

Commit

Permalink
refactor(axes): convert tests to typescript
Browse files Browse the repository at this point in the history
  • Loading branch information
wyze committed Apr 30, 2021
1 parent eb969df commit 7d01a53
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 85 deletions.
10 changes: 5 additions & 5 deletions packages/axes/src/canvas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { degreesToRadians, CompleteTheme } from '@nivo/core'
import { computeCartesianTicks, getFormatter, computeGridLines } from './compute'
import { TicksSpec, AllScales, AxisLegendPosition, CanvasAxisProp, ValueFormatter } from './types'
import { TicksSpec, AnyScale, AxisLegendPosition, CanvasAxisProp, ValueFormatter } from './types'

export const renderAxisToCanvas = <Value>(
ctx: CanvasRenderingContext2D,
Expand All @@ -25,7 +25,7 @@ export const renderAxisToCanvas = <Value>(
theme,
}: {
axis: 'x' | 'y'
scale: AllScales
scale: AnyScale
x?: number
y?: number
length: number
Expand Down Expand Up @@ -168,8 +168,8 @@ export const renderAxesToCanvas = <X, Y>(

theme,
}: {
xScale: AllScales
yScale: AllScales
xScale: AnyScale
yScale: AnyScale
width: number
height: number
top?: CanvasAxisProp<X>
Expand Down Expand Up @@ -216,7 +216,7 @@ export const renderGridLinesToCanvas = <Value>(
}: {
width: number
height: number
scale: AllScales
scale: AnyScale
axis: 'x' | 'y'
values?: TicksSpec<Value>
}
Expand Down
6 changes: 3 additions & 3 deletions packages/axes/src/components/Axes.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import { Axis } from './Axis'
import { AllScales, AxisProp } from '../types'
import { AnyScale, AxisProp } from '../types'

const positions = ['top', 'right', 'bottom', 'left'] as const

Expand All @@ -14,8 +14,8 @@ export const Axes = <X extends number | string | Date, Y extends number | string
bottom,
left,
}: {
xScale: AllScales
yScale: AllScales
xScale: AnyScale
yScale: AnyScale
width: number
height: number
top?: AxisProp<X>
Expand Down
4 changes: 2 additions & 2 deletions packages/axes/src/components/Axis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ export const Axis = <Scale,>({
legendOffset = 0,
onClick,
ariaHidden,
}: AxisProps<Scale>) => {
}: AxisProps) => {
const theme = useTheme()

const formatValue = useMemo(() => getFormatter(format, scale as any), [format, scale])
const formatValue = useMemo(() => getFormatter(format, scale), [format, scale])

const { ticks, textAlign, textBaseline } = computeCartesianTicks({
axis,
Expand Down
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 } from 'react'
import { GridLines } from './GridLines'
import { computeGridLines } from '../compute'
import { AllScales } from '../types'
import { AnyScale } from '../types'

export const Grid = <X extends number | string | Date, Y extends number | string | Date>({
width,
Expand All @@ -13,9 +13,9 @@ export const Grid = <X extends number | string | Date, Y extends number | string
}: {
width: number
height: number
xScale?: AllScales
xScale?: AnyScale
xValues?: number | X[]
yScale?: AllScales
yScale?: AnyScale
yValues?: number | Y[]
}) => {
const xLines = useMemo(() => {
Expand Down
47 changes: 26 additions & 21 deletions packages/axes/src/compute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { timeFormat } from 'd3-time-format'
import { format as d3Format } from 'd3-format'
// @ts-ignore
import { textPropsByEngine } from '@nivo/core'
import { Point, TicksSpec, AllScales, ScaleWithBandwidth, ValueFormatter, Line } from './types'
import { Point, TicksSpec, AnyScale, ScaleWithBandwidth, ValueFormatter, Line } from './types'

export const centerScale = (scale: ScaleWithBandwidth) => {
const bandwidth = scale.bandwidth()
Expand All @@ -47,7 +47,7 @@ export const centerScale = (scale: ScaleWithBandwidth) => {
offset = Math.round(offset)
}

return (d: unknown) => (scale(d) ?? 0) + offset
return <T>(d: T) => (scale(d) ?? 0) + offset
}

const timeByType: Record<string, [CountableTimeInterval, CountableTimeInterval]> = {
Expand Down Expand Up @@ -76,14 +76,14 @@ const isInteger = (value: unknown): value is number =>

const isArray = <T>(value: unknown): value is T[] => Array.isArray(value)

export const getScaleTicks = <Value>(scale: AllScales, spec?: TicksSpec<Value>) => {
export const getScaleTicks = <Value>(scale: AnyScale, spec?: TicksSpec<Value>) => {
// specific values
if (Array.isArray(spec)) {
return spec
}

// continuous scales
if (scale.ticks) {
if ('ticks' in scale) {
// default behaviour
if (spec === undefined) {
return scale.ticks()
Expand All @@ -94,19 +94,24 @@ export const getScaleTicks = <Value>(scale: AllScales, spec?: TicksSpec<Value>)
return scale.ticks(spec)
}

if (typeof spec === 'string') {
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)
}

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

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

throw new Error(`Invalid tickValues: ${spec}`)
Expand All @@ -122,30 +127,30 @@ export const computeCartesianTicks = <Value>({
scale,
ticksPosition,
tickValues,
tickSize,
tickPadding,
tickRotation,
tickSize = NaN,
tickPadding = NaN,
tickRotation = NaN,
engine = 'svg',
}: {
axis: 'x' | 'y'
scale: AllScales
ticksPosition: 'after' | 'before'
scale: AnyScale
ticksPosition?: 'after' | 'before'
tickValues?: TicksSpec<Value>
tickSize: number
tickPadding: number
tickRotation: number
tickSize?: number
tickPadding?: number
tickRotation?: number
engine?: 'svg' | 'canvas'
}) => {
const values = getScaleTicks(scale, tickValues) as Array<string | number>
const values = getScaleTicks(scale, tickValues)

const textProps = textPropsByEngine[engine]

const position = 'bandwidth' in scale ? centerScale(scale as any) : scale
const position = 'bandwidth' in scale ? centerScale(scale) : scale
const line = { lineX: 0, lineY: 0 }
const text = { textX: 0, textY: 0 }

const isRTL = typeof document === 'object' ? document.dir === 'rtl' : false
let translate: (value: unknown) => Point
let translate: (value: string | number) => Point
let textAlign: CanvasTextAlign = textProps.align.center
let textBaseline: CanvasTextBaseline = textProps.baseline.center

Expand Down Expand Up @@ -206,7 +211,7 @@ export const computeCartesianTicks = <Value>({

export const getFormatter = (
format: string | ValueFormatter,
scale: AllScales
scale: AnyScale
): ValueFormatter | undefined => {
if (typeof format === 'undefined' || typeof format === 'function') return format

Expand All @@ -227,16 +232,16 @@ export const computeGridLines = <Value>({
}: {
width: number
height: number
scale: AllScales
scale: AnyScale
axis: 'x' | 'y'
values?: TicksSpec<Value>
}) => {
const lineValues = isArray<Value>(_values) ? _values : undefined
const lineValues = isArray<number>(_values) ? _values : undefined
const lineCount = isInteger(_values) ? _values : undefined

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

const position = 'bandwidth' in scale ? centerScale(scale as any) : scale
const position = 'bandwidth' in scale ? centerScale(scale) : scale

const lines: Line[] = values.map(value => {
const key = `${value}`
Expand Down
61 changes: 22 additions & 39 deletions packages/axes/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { SpringValues } from 'react-spring'
import { ScaleBand, ScaleLinear, ScalePoint } from 'd3-scale'
import {
ScaleBand,
ScaleLinear,
ScaleOrdinal,
ScalePoint,
ScaleTime,
ScaleSymLog,
ScaleLogarithmic,
} from 'd3-scale'
import React from 'react'

// export type AxisValue = string | number | Date
Expand All @@ -19,10 +27,17 @@ export type Point = {
y: number
}

export type AnyScale<Range, Output, Unknown> =
| ScaleLinear<Range, Output, Unknown>
| ScaleBand<Range>
| ScalePoint<Range>
export type ScaleWithBandwidth =
| (ScaleBand<any> & { type: 'band' })
| (ScalePoint<any> & { type: 'point' })

export type AnyScale =
| (ScaleLinear<any, any> & { type: 'linear' })
| (ScaleOrdinal<any, any> & { type: 'ordinal' })
| (ScaleTime<any, any> & { useUTC: boolean; type: 'time' })
| (ScaleSymLog<any, any> & { type: 'symlog' })
| (ScaleLogarithmic<any, any> & { type: 'log' })
| ScaleWithBandwidth

export type TicksSpec<Value> =
// exact number of ticks, please note that
Expand All @@ -36,37 +51,6 @@ export type TicksSpec<Value> =
// override scale ticks with custom explicit values
| Value[]

type NumberValue = number | { valueOf(): number }

interface CommonScale {
domain(): unknown[]
ticks<Count>(count?: Count): number[]

useUTC?: boolean
// type: 'linear' | 'symlog' | 'log' | 'point' | 'time' | 'band'
}

export interface OtherScale extends CommonScale {
(value: unknown): number | undefined
<Output>(value: NumberValue): Output | undefined
<Output>(value: NumberValue | Date): Output | undefined
<Domain extends string | number | Date, Output>(value: Domain): Output | undefined
<Domain extends { toString(): string }, Output>(value: Domain): Output | undefined
(value: NumberValue): number | undefined

bandwidth: never
type: 'linear' | 'symlog' | 'log' | 'time'
}

export interface ScaleWithBandwidth extends CommonScale {
<Domain>(domain: Domain): number | undefined
bandwidth(): number
round(): boolean
type: 'band' | 'point'
}

export type AllScales = ScaleWithBandwidth | OtherScale

export type AxisLegendPosition = 'start' | 'middle' | 'end'

export interface AxisProp<Value> {
Expand All @@ -86,10 +70,9 @@ export interface CanvasAxisProp<Value> extends Omit<AxisProp<Value>, 'legend'> {
legend?: string
}

export interface AxisProps<Scale = unknown> {
export interface AxisProps {
axis: 'x' | 'y'
// scale: AllScales
scale: Scale
scale: AnyScale
x?: number
y?: number
length: number
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
/*
* This file is part of the nivo project.
*
* Copyright 2016-present, Raphaël Benitte.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { scaleLinear, scaleOrdinal, scalePoint, scaleBand, scaleTime, scaleUtc } from 'd3-scale'
import { getScaleTicks, computeCartesianTicks } from '../src/compute'

Expand Down Expand Up @@ -183,7 +175,8 @@ describe('getTicks', () => {
it(`should support ${interval.interval} interval`, () => {
const intervalTimeScale = scaleUtc().domain(interval.domain)

intervalTimeScale.useUTC = true
// set utc flag on our scale
;(intervalTimeScale as any).useUTC = true

expect(getScaleTicks(intervalTimeScale, `every ${interval.interval}`)).toEqual(
interval.expect
Expand Down
11 changes: 9 additions & 2 deletions packages/bullet/src/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { scaleLinear } from 'd3-scale'
import { ScaleLinear, scaleLinear } from 'd3-scale'
import { useMemo } from 'react'
import { Datum, CommonBulletProps } from './types'

Expand All @@ -20,14 +20,21 @@ export const useEnhancedData = (

const min = Math.min(...all, 0)

const scale = scaleLinear().domain([min, max])
const scale = scaleLinear().domain([min, max]) as ScaleLinear<
number,
number,
never
> & { type: 'linear' }

if (layout === 'horizontal') {
scale.range(reverse === true ? [width, 0] : [0, width])
} else {
scale.range(reverse === true ? [0, height] : [height, 0])
}

// Add our type property
;(scale as any).type = 'linear'

return {
...d,
scale,
Expand Down
2 changes: 1 addition & 1 deletion packages/bullet/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface Datum {
}

export type EnhancedDatum = Datum & {
scale: ScaleLinear<number, number, never>
scale: ScaleLinear<number, number, never> & { type: 'linear' }
}

export interface ComputedRangeDatum {
Expand Down

0 comments on commit 7d01a53

Please sign in to comment.