From 047140d2513975402df8dc046bb7b2a07d4904af Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Fri, 17 Jul 2020 16:34:44 -0700 Subject: [PATCH 01/42] feat: update vx scale types --- packages/vx-scale/package.json | 6 +- .../vx-scale/src/mixins/applyInterpolate.ts | 15 ++ packages/vx-scale/src/mixins/applyRound.ts | 17 ++ packages/vx-scale/src/mixins/applyZero.ts | 16 ++ packages/vx-scale/src/scales/linear.ts | 48 +++--- packages/vx-scale/src/scales/log.ts | 50 +++--- packages/vx-scale/src/scales/power.ts | 50 +++--- packages/vx-scale/src/scales/squareRoot.ts | 30 +++- packages/vx-scale/src/types/Base.ts | 8 + .../vx-scale/src/types/BaseScaleConfig.ts | 130 ++++++++++++++++ packages/vx-scale/src/types/Nice.ts | 1 + packages/vx-scale/src/types/Scale.ts | 37 +++++ packages/vx-scale/src/types/ScaleConfig.ts | 145 ++++++++++++++++++ .../vx-scale/src/types/ScaleInterpolate.ts | 14 ++ .../src/util/createColorInterpolator.ts | 43 ++++++ yarn.lock | 32 +++- 16 files changed, 549 insertions(+), 93 deletions(-) create mode 100644 packages/vx-scale/src/mixins/applyInterpolate.ts create mode 100644 packages/vx-scale/src/mixins/applyRound.ts create mode 100644 packages/vx-scale/src/mixins/applyZero.ts create mode 100644 packages/vx-scale/src/types/Base.ts create mode 100644 packages/vx-scale/src/types/BaseScaleConfig.ts create mode 100644 packages/vx-scale/src/types/Nice.ts create mode 100644 packages/vx-scale/src/types/Scale.ts create mode 100644 packages/vx-scale/src/types/ScaleConfig.ts create mode 100644 packages/vx-scale/src/types/ScaleInterpolate.ts create mode 100644 packages/vx-scale/src/util/createColorInterpolator.ts diff --git a/packages/vx-scale/package.json b/packages/vx-scale/package.json index 24268ff86..130ca85a2 100644 --- a/packages/vx-scale/package.json +++ b/packages/vx-scale/package.json @@ -21,7 +21,7 @@ "visualizations", "charts" ], - "author": "@hshoff", + "authors": ["@hshoff", "@kristw"], "license": "MIT", "bugs": { "url": "https://github.com/hshoff/vx/issues" @@ -29,7 +29,9 @@ "homepage": "https://github.com/hshoff/vx#readme", "dependencies": { "@types/d3-scale": "^2.1.1", - "d3-scale": "^2.2.2" + "@types/d3-interpolate": "^1.3.1", + "d3-scale": "^3.0.1", + "d3-interpolate": "^1.4.0" }, "publishConfig": { "access": "public" diff --git a/packages/vx-scale/src/mixins/applyInterpolate.ts b/packages/vx-scale/src/mixins/applyInterpolate.ts new file mode 100644 index 000000000..6b14018ea --- /dev/null +++ b/packages/vx-scale/src/mixins/applyInterpolate.ts @@ -0,0 +1,15 @@ +import { InterpolatorFactory } from 'd3-scale'; +import { Value } from '../types/Base'; +import { D3Scale } from '../types/Scale'; +import { ScaleInterpolate, ScaleInterpolateParams } from '../types/ScaleInterpolate'; +import createColorInterpolator from '../util/createColorInterpolator'; + +export default function applyInterpolate( + scale: D3Scale, + config: { interpolate?: ScaleInterpolate | ScaleInterpolateParams }, +) { + if (config.interpolate && 'interpolate' in scale) { + const interpolator = createColorInterpolator(config.interpolate); + scale.interpolate((interpolator as unknown) as InterpolatorFactory); + } +} diff --git a/packages/vx-scale/src/mixins/applyRound.ts b/packages/vx-scale/src/mixins/applyRound.ts new file mode 100644 index 000000000..da278f35e --- /dev/null +++ b/packages/vx-scale/src/mixins/applyRound.ts @@ -0,0 +1,17 @@ +import { interpolateRound } from 'd3-interpolate'; +import { InterpolatorFactory } from 'd3-scale'; +import { Value } from '../types/Base'; +import { D3Scale } from '../types/Scale'; + +export default function applyRound( + scale: D3Scale, + config: { round?: boolean }, +) { + if (typeof config.round !== 'undefined') { + if ('round' in scale) { + scale.round(config.round); + } else if ('interpolate' in scale && config.round) { + scale.interpolate((interpolateRound as unknown) as InterpolatorFactory); + } + } +} diff --git a/packages/vx-scale/src/mixins/applyZero.ts b/packages/vx-scale/src/mixins/applyZero.ts new file mode 100644 index 000000000..c09f77c82 --- /dev/null +++ b/packages/vx-scale/src/mixins/applyZero.ts @@ -0,0 +1,16 @@ +import { Value } from '../types/Base'; +import { SomeD3Scale } from '../types/Scale'; + +export default function applyZero( + scale: SomeD3Scale<'linear' | 'pow' | 'sqrt' | 'symlog', Output>, + config: { zero?: boolean }, +) { + if (config.zero === true) { + const domain = scale.domain() as number[]; + const [a, b] = domain; + const isDescending = b < a; + const [min, max] = isDescending ? [b, a] : [a, b]; + const domainWithZero = [Math.min(0, min), Math.max(0, max)]; + scale.domain(isDescending ? domainWithZero.reverse() : domainWithZero); + } +} diff --git a/packages/vx-scale/src/scales/linear.ts b/packages/vx-scale/src/scales/linear.ts index e127f8d26..449692d06 100644 --- a/packages/vx-scale/src/scales/linear.ts +++ b/packages/vx-scale/src/scales/linear.ts @@ -1,35 +1,35 @@ import { scaleLinear } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { PickD3Scale } from '../types/Scale'; +import applyInterpolate from '../mixins/applyInterpolate'; +import applyRound from '../mixins/applyRound'; +import applyZero from '../mixins/applyZero'; -export type LinearConfig = { - /** Sets the input values of the scale, which are numbers for a linear scale. */ - domain?: number[]; - /** Sets the output values of the scale. */ - range?: Output[]; - /** Sets the output values of the scale while setting its interpolator to round. If the elements are not numbers, they will be coerced to numbers. */ - rangeRound?: number[]; - /** Extends the domain so that it starts and ends on nice round values. */ - nice?: boolean; - /** Whether the scale should clamp values to within the range. */ - clamp?: boolean; -}; +export function updateLinearScale( + scale: PickD3Scale<'linear', Output>, + config: PickScaleConfigWithoutType<'linear', Output>, +) { + const { domain, range, clamp = true, nice = true } = config; -export default function linearScale({ - range, - rangeRound, - domain, - nice = false, - clamp = false, -}: LinearConfig) { - const scale = scaleLinear(); - - if (range) scale.range(range); - if (rangeRound) scale.rangeRound(rangeRound); if (domain) scale.domain(domain); if (nice) scale.nice(); - if (clamp) scale.clamp(true); + if (range) scale.range(range); + scale.clamp(clamp); + applyInterpolate(scale, config); + applyRound(scale, config); + applyZero(scale, config); + + // TODO: Remove? // @ts-ignore scale.type = 'linear'; return scale; } + +export default function createLinearScale( + config: PickScaleConfigWithoutType<'linear', Output>, +) { + return updateLinearScale(scaleLinear(), config); +} diff --git a/packages/vx-scale/src/scales/log.ts b/packages/vx-scale/src/scales/log.ts index 1d770bd8d..15fe1cf23 100644 --- a/packages/vx-scale/src/scales/log.ts +++ b/packages/vx-scale/src/scales/log.ts @@ -1,39 +1,33 @@ import { scaleLog } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickD3Scale } from '../types/Scale'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import applyInterpolate from '../mixins/applyInterpolate'; +import applyRound from '../mixins/applyRound'; -export type LogConfig = { - /** Sets the input values of the scale, which are numbers for a log scale. */ - domain?: number[]; - /** Sets the output values of the scale. */ - range?: Output[]; - /** Sets the output values of the scale while setting its interpolator to round. If the elements are not numbers, they will be coerced to numbers. */ - rangeRound?: number[]; - /** Sets the base for this logarithmic scale (defaults to 10). */ - base?: number; - /** Extends the domain so that it starts and ends on nice round values. */ - nice?: boolean; - /** Whether the scale should clamp values to within the range. */ - clamp?: boolean; -}; +export function updateLogScale( + scale: PickD3Scale<'log', Output>, + config: PickScaleConfigWithoutType<'log', Output>, +) { + const { domain, range, base, clamp = true, nice = true } = config; -export default function logScale({ - range, - rangeRound, - domain, - base, - nice = false, - clamp = false, -}: LogConfig) { - const scale = scaleLog(); - - if (range) scale.range(range); - if (rangeRound) scale.rangeRound(rangeRound); + if (base) scale.base(base); if (domain) scale.domain(domain); if (nice) scale.nice(); - if (clamp) scale.clamp(true); - if (base) scale.base(base); + if (range) scale.range(range); + + scale.clamp(clamp); + applyInterpolate(scale, config); + applyRound(scale, config); // @ts-ignore scale.type = 'log'; return scale; } + +export default function createLogScale( + config: PickScaleConfigWithoutType<'log', Output>, +) { + return updateLogScale(scaleLog(), config); +} diff --git a/packages/vx-scale/src/scales/power.ts b/packages/vx-scale/src/scales/power.ts index 5c40c46ea..9d718b41d 100644 --- a/packages/vx-scale/src/scales/power.ts +++ b/packages/vx-scale/src/scales/power.ts @@ -1,39 +1,35 @@ import { scalePow } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickD3Scale } from '../types/Scale'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import applyInterpolate from '../mixins/applyInterpolate'; +import applyRound from '../mixins/applyRound'; +import applyZero from '../mixins/applyZero'; -export type PowerConfig = { - /** Sets the output values of the scale. */ - range?: Output[]; - /** Sets the output values of the scale while setting its interpolator to round. They need not be numbers, though numbers are required for invert. */ - rangeRound?: number[]; - /** Sets the input values of the scalem which are numbers for a power scale. */ - domain?: number[]; - /** Sets the scale's exponent to the given number, defaults to 1. This is effectively a linear scale until you set a different exponent. */ - exponent?: number; - /** Extends the domain so that it starts and ends on nice round values. */ - nice?: boolean; - /** Whether the scale should clamp values to within the range. */ - clamp?: boolean; -}; +export function updatePowScale( + scale: PickD3Scale<'pow', Output>, + config: PickScaleConfigWithoutType<'pow', Output>, +) { + const { domain, range, clamp = true, exponent, nice = true } = config; -export default function powerScale({ - range, - rangeRound, - domain, - exponent, - nice = false, - clamp = false, -}: PowerConfig) { - const scale = scalePow(); - - if (range) scale.range(range); - if (rangeRound) scale.rangeRound(rangeRound); if (domain) scale.domain(domain); if (nice) scale.nice(); - if (clamp) scale.clamp(true); + if (range) scale.range(range); + + scale.clamp(clamp); if (exponent) scale.exponent(exponent); + applyInterpolate(scale, config); + applyRound(scale, config); + applyZero(scale, config); // @ts-ignore scale.type = 'power'; return scale; } + +export default function createPowScale( + config: PickScaleConfigWithoutType<'pow', Output>, +) { + return updatePowScale(scalePow(), config); +} diff --git a/packages/vx-scale/src/scales/squareRoot.ts b/packages/vx-scale/src/scales/squareRoot.ts index e08c09e00..498c95be8 100644 --- a/packages/vx-scale/src/scales/squareRoot.ts +++ b/packages/vx-scale/src/scales/squareRoot.ts @@ -1,12 +1,34 @@ -import powerScale, { PowerConfig } from './power'; +import { scaleSqrt } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickD3Scale } from '../types/Scale'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import applyInterpolate from '../mixins/applyInterpolate'; +import applyRound from '../mixins/applyRound'; +import applyZero from '../mixins/applyZero'; -export type SquareRootConfig = Omit, 'exponent'>; +export function updateSqrtScale( + scale: PickD3Scale<'sqrt', Output>, + config: PickScaleConfigWithoutType<'sqrt', Output>, +) { + const { domain, range, clamp = true, nice = true } = config; -export default function squareRootScale(scaleConfig: SquareRootConfig) { - const scale = powerScale({ ...scaleConfig, exponent: 0.5 }); + if (domain) scale.domain(domain); + if (nice) scale.nice(); + if (range) scale.range(range); + + scale.clamp(clamp); + applyInterpolate(scale, config); + applyRound(scale, config); + applyZero(scale, config); // @ts-ignore scale.type = 'squareRoot'; return scale; } + +export default function createSqrtScale( + config: PickScaleConfigWithoutType<'sqrt', Output>, +) { + return updateSqrtScale(scaleSqrt(), config); +} diff --git a/packages/vx-scale/src/types/Base.ts b/packages/vx-scale/src/types/Base.ts new file mode 100644 index 000000000..479716295 --- /dev/null +++ b/packages/vx-scale/src/types/Base.ts @@ -0,0 +1,8 @@ +/** A value that has .toString() function */ +export type HasToString = { toString(): string }; + +/** Possible values */ +export type Value = number | string | boolean | null; + +/** Union types of all values from a map type */ +export type ValueOf = T[keyof T]; diff --git a/packages/vx-scale/src/types/BaseScaleConfig.ts b/packages/vx-scale/src/types/BaseScaleConfig.ts new file mode 100644 index 000000000..7ec562a32 --- /dev/null +++ b/packages/vx-scale/src/types/BaseScaleConfig.ts @@ -0,0 +1,130 @@ +import { ScaleInterpolate, ScaleInterpolateParams } from './ScaleInterpolate'; + +export interface BaseScaleConfig { + type: T; + + /** + * The domain of the scale. + */ + domain?: D; + + /** + * The range of the scale. + */ + range?: R; + + /** + * The alignment of the steps within the scale range. + * + * This value must lie in the range `[0,1]`. A value of `0.5` indicates that the steps should be centered within the range. A value of `0` or `1` may be used to shift the bands to one side, say to position them adjacent to an axis. + * + * __Default value:__ `0.5` + */ + align?: number; + + /** + * The logarithm base of the `log` scale (default `10`). + */ + base?: number; + + /** + * If `true`, values that exceed the data domain are clamped to either the minimum or maximum range value + * + * __Default value:__ `true`. + */ + clamp?: boolean; + + /** + * A constant determining the slope of the symlog function around zero. Only used for `symlog` scales. + * + * __Default value:__ `1` + */ + constant?: number; + + /** + * The exponent of the `pow` scale. + */ + exponent?: number; + + /** + * The interpolation method for range values. + * By default, a general interpolator for numbers, dates, strings and colors (in HCL space) is used. + * For color ranges, this property allows interpolation in alternative color spaces. Legal values include `rgb`, `hsl`, `hsl-long`, `lab`, `hcl`, `hcl-long`, `cubehelix` and `cubehelix-long` ('-long' variants use longer paths in polar coordinate spaces). If object-valued, this property accepts an object with a string-valued _type_ property and an optional numeric _gamma_ property applicable to rgb and cubehelix interpolators. For more, see the [d3-interpolate documentation](https://github.com/d3/d3-interpolate). + * + * * __Default value:__ `hcl` + */ + interpolate?: ScaleInterpolate | ScaleInterpolateParams; + + /** + * Extending the domain so that it starts and ends on nice round values. This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. Nicing is useful if the domain is computed from data and may be irregular. For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2, 1.0]_. + * + * For quantitative scales such as linear, `nice` can be either a boolean flag or a number. If `nice` is a number, it will represent a desired tick count. This allows greater control over the step size used to extend the bounds, guaranteeing that the returned ticks will exactly cover the domain. + * + * __Default value:__ `true` for _quantitative_ fields; `false` otherwise. + * + */ + nice?: boolean | number; + + /** + * For _[continuous](https://vega.github.io/vega-lite/docs/scale.html#continuous)_ scales, expands the scale domain to accommodate the specified number of pixels on each of the scale range. The scale range must represent pixels for this parameter to function as intended. + * Padding adjustment is performed prior to all other adjustments, including the effects of the `zero`, `nice` properties. + * + * For _[band](https://vega.github.io/vega-lite/docs/scale.html#band)_ scales, shortcut for setting `paddingInner` and `paddingOuter` to the same value. + * + * For _[point](https://vega.github.io/vega-lite/docs/scale.html#point)_ scales, alias for `paddingOuter`. + * + * __Default value:__ For _continuous_ scales, derived from the [scale config](https://vega.github.io/vega-lite/docs/scale.html#config)'s `continuousPadding`. + * For _band and point_ scales, see `paddingInner` and `paddingOuter`. By default, Vega-Lite sets padding such that _width/height = number of unique values * step_. + * + * @minimum 0 + */ + padding?: number; + + /** + * The inner padding (spacing) within each band step of band scales, as a fraction of the step size. This value must lie in the range [0,1]. + * + * For point scale, this property is invalid as point scales do not have internal band widths (only step sizes between bands). + * + * __Default value:__ derived from the [scale config](https://vega.github.io/vega-lite/docs/scale.html#config)'s `bandPaddingInner`. + * + * @minimum 0 + * @maximum 1 + */ + paddingInner?: number; + + /** + * The outer padding (spacing) at the ends of the range of band and point scales, + * as a fraction of the step size. This value must lie in the range [0,1]. + * + * __Default value:__ derived from the [scale config](https://vega.github.io/vega-lite/docs/scale.html#config)'s `bandPaddingOuter` for band scales and `pointPadding` for point scales. + * By default, Vega-Lite sets outer padding such that _width/height = number of unique values * step_. + * + * @minimum 0 + * @maximum 1 + */ + paddingOuter?: number; + + /** + * If true, reverses the order of the scale range. + * __Default value:__ `false`. + * + * @hidden + */ + reverse?: boolean; + + /** + * If `true`, rounds numeric output values to integers. This can be helpful for snapping to the pixel grid. + * + * __Default value:__ `false`. + */ + round?: boolean; + + /** + * If `true`, ensures that a zero baseline value is included in the scale domain. + * + * __Default value:__ `true` for x and y channels if the quantitative field is not binned and no custom `domain` is provided; `false` otherwise. + * + * __Note:__ Log, time, and utc scales do not support `zero`. + */ + zero?: boolean; +}; diff --git a/packages/vx-scale/src/types/Nice.ts b/packages/vx-scale/src/types/Nice.ts new file mode 100644 index 000000000..9a76dbedf --- /dev/null +++ b/packages/vx-scale/src/types/Nice.ts @@ -0,0 +1 @@ +export type NiceTime = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; diff --git a/packages/vx-scale/src/types/Scale.ts b/packages/vx-scale/src/types/Scale.ts new file mode 100644 index 000000000..0d38edfac --- /dev/null +++ b/packages/vx-scale/src/types/Scale.ts @@ -0,0 +1,37 @@ +import { + ScaleOrdinal, + ScaleLinear, + ScaleLogarithmic, + ScalePower, + ScaleTime, + ScaleQuantile, + ScaleQuantize, + ScaleThreshold, + ScalePoint, + ScaleBand, + ScaleSymLog, +} from 'd3-scale'; +import { HasToString, Value, ValueOf } from './Base'; + +export interface ScaleTypeToD3Scale { + linear: ScaleLinear; + log: ScaleLogarithmic; + pow: ScalePower; + sqrt: ScalePower; + symlog: ScaleSymLog; + time: ScaleTime; + utc: ScaleTime; + quantile: ScaleQuantile; + quantize: ScaleQuantize; + threshold: ScaleThreshold; + ordinal: ScaleOrdinal; + point: ScalePoint; + band: ScaleBand; +} + +export type PickD3Scale< + T extends keyof ScaleTypeToD3Scale, + Output extends Value = Value +> = ValueOf, T>>; + +export type D3Scale = ValueOf>; diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts new file mode 100644 index 000000000..54cbbb57c --- /dev/null +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -0,0 +1,145 @@ +import { BaseScaleConfig } from './BaseScaleConfig'; +import { HasToString, Value, ValueOf } from './Base'; +import { NiceTime } from './Nice'; + +export type TimeInput = string | number | Date; +export type ContinuousInput = number | Date; +export type DiscreteInput = HasToString; + +export type TimeDomain = TimeInput[]; +export type ContinuousDomain = ContinuousInput[]; + +// Make the specific scales pick +// from same base type to share property documentation +// (which is useful for auto-complete/intellisense) +// and add `type` property as discriminant of union type. +type CreateScaleConfig> = Pick< + BaseScaleConfig, + 'type' | 'domain' | 'range' | 'reverse' | Fields +>; + +export type LinearScaleConfig = CreateScaleConfig< + 'linear', + Output[], + ContinuousDomain, + 'clamp' | 'interpolate' | 'nice' | 'round' | 'zero' +>; + +export type LogScaleConfig = CreateScaleConfig< + 'log', + Output[], + ContinuousDomain, + 'base' | 'clamp' | 'interpolate' | 'nice' | 'round' +>; + +export type PowScaleConfig = CreateScaleConfig< + 'pow', + Output[], + ContinuousDomain, + 'clamp' | 'exponent' | 'interpolate' | 'nice' | 'round' | 'zero' +>; + +export type SqrtScaleConfig = CreateScaleConfig< + 'sqrt', + Output[], + ContinuousDomain, + 'clamp' | 'interpolate' | 'nice' | 'round' | 'zero' +>; + +export type SymlogScaleConfig = CreateScaleConfig< + 'symlog', + Output[], + ContinuousDomain, + 'clamp' | 'constant' | 'nice' | 'round' | 'zero' +>; + +export type QuantileScaleConfig = CreateScaleConfig< + 'quantile', + Output[], + ContinuousDomain, + 'interpolate' +>; + +export type QuantizeScaleConfig = CreateScaleConfig< + 'quantize', + Output[], + ContinuousDomain, + 'interpolate' | 'nice' | 'zero' +>; + +export type ThresholdScaleConfig = CreateScaleConfig< + 'threshold', + Output[], + ContinuousDomain, + 'interpolate' | 'nice' +>; + +export type OrdinalScaleConfig = CreateScaleConfig< + 'ordinal', + Output[], + DiscreteInput[], + 'interpolate' +>; + +export type PointScaleConfig = CreateScaleConfig< + 'point', + Output[], + DiscreteInput[], + 'align' | 'padding' | 'round' +>; + +export type BandScaleConfig = CreateScaleConfig< + 'band', + Output[], + DiscreteInput[], + 'align' | 'padding' | 'paddingInner' | 'paddingOuter' | 'round' +>; + +interface TemporalScaleConfig + extends CreateScaleConfig { + /** + * Extending the domain so that it starts and ends on nice round values. This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. Nicing is useful if the domain is computed from data and may be irregular. For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2, 1.0]_. + * + * For quantitative scales such as linear, `nice` can be either a boolean flag or a number. If `nice` is a number, it will represent a desired tick count. This allows greater control over the step size used to extend the bounds, guaranteeing that the returned ticks will exactly cover the domain. + * + * For temporal fields with time and utc scales, the `nice` value can be a string indicating the desired time interval. Legal values are `"millisecond"`, `"second"`, `"minute"`, `"hour"`, `"day"`, `"week"`, `"month"`, and `"year"`. Alternatively, `time` and `utc` scales can accept an object-valued interval specifier of the form `{"interval": "month", "step": 3}`, which includes a desired number of interval steps. Here, the domain would snap to quarter (Jan, Apr, Jul, Oct) boundaries. + * + * __Default value:__ `true` for unbinned _quantitative_ fields; `false` otherwise. + * + */ + nice?: boolean | number | NiceTime | { interval: NiceTime; step: number }; +} + +export type TimeScaleConfig = TemporalScaleConfig<'time', Output>; + +export type UtcScaleConfig = TemporalScaleConfig<'utc', Output>; + +export interface ScaleTypeToScaleConfig { + linear: LinearScaleConfig; + log: LogScaleConfig; + pow: PowScaleConfig; + sqrt: SqrtScaleConfig; + symlog: SymlogScaleConfig; + time: TimeScaleConfig; + utc: UtcScaleConfig; + quantile: QuantileScaleConfig; + quantize: QuantizeScaleConfig; + threshold: ThresholdScaleConfig; + ordinal: OrdinalScaleConfig; + point: PointScaleConfig; + band: BandScaleConfig; +} + +export type PickScaleConfig< + T extends keyof ScaleTypeToScaleConfig, + Output extends Value = Value +> = ValueOf, T>>; + +export type PickScaleConfigWithoutType< + T extends keyof ScaleTypeToScaleConfig, + Output extends Value = Value +> = Omit, 'type'>; + +export type ScaleConfig = ValueOf>; + +export type ScaleType = keyof ScaleTypeToScaleConfig; diff --git a/packages/vx-scale/src/types/ScaleInterpolate.ts b/packages/vx-scale/src/types/ScaleInterpolate.ts new file mode 100644 index 000000000..8d72fe443 --- /dev/null +++ b/packages/vx-scale/src/types/ScaleInterpolate.ts @@ -0,0 +1,14 @@ +export type ScaleInterpolate = + | 'rgb' + | 'lab' + | 'hcl' + | 'hsl' + | 'hsl-long' + | 'hcl-long' + | 'cubehelix' + | 'cubehelix-long'; + +export interface ScaleInterpolateParams { + type: 'rgb' | 'cubehelix' | 'cubehelix-long'; + gamma?: number; +} diff --git a/packages/vx-scale/src/util/createColorInterpolator.ts b/packages/vx-scale/src/util/createColorInterpolator.ts new file mode 100644 index 000000000..c3f482e85 --- /dev/null +++ b/packages/vx-scale/src/util/createColorInterpolator.ts @@ -0,0 +1,43 @@ +import { + interpolateRgb, + interpolateLab, + interpolateHcl, + interpolateHclLong, + interpolateHsl, + interpolateHslLong, + interpolateCubehelix, + interpolateCubehelixLong, +} from 'd3-interpolate'; +import { ScaleInterpolateParams, ScaleInterpolate } from '../types/ScaleInterpolate'; + +const interpolatorMap = { + lab: interpolateLab, + hcl: interpolateHcl, + 'hcl-long': interpolateHclLong, + hsl: interpolateHsl, + 'hsl-long': interpolateHslLong, + cubehelix: interpolateCubehelix, + 'cubehelix-long': interpolateCubehelixLong, + rgb: interpolateRgb, +} as const; + +export default function createColorInterpolator( + interpolate: ScaleInterpolate | ScaleInterpolateParams, +) { + switch (interpolate) { + case 'lab': + case 'hcl': + case 'hcl-long': + case 'hsl': + case 'hsl-long': + case 'cubehelix': + case 'cubehelix-long': + case 'rgb': + return interpolatorMap[interpolate]; + default: + } + + const { type, gamma } = interpolate; + const interpolator = interpolatorMap[type]; + return typeof gamma === 'undefined' ? interpolator : interpolator.gamma(gamma); +} diff --git a/yarn.lock b/yarn.lock index b8bae5166..032bb1763 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2678,6 +2678,11 @@ resolved "https://registry.yarnpkg.com/@types/d3-chord/-/d3-chord-1.0.9.tgz#ccc5de03ff079025491b7aa6b750670a140b45ae" integrity sha512-UA6lI9CVW5cT5Ku/RV4hxoFn4mKySHm7HEgodtfRthAj1lt9rKZEPon58vyYfk+HIAm33DtJJgZwMXy2QgyPXw== +"@types/d3-color@*": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.2.2.tgz#80cf7cfff7401587b8f89307ba36fe4a576bc7cf" + integrity sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw== + "@types/d3-format@^1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/d3-format/-/d3-format-1.3.1.tgz#35bf88264bd6bcda39251165bb827f67879c4384" @@ -2695,6 +2700,13 @@ resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz#4c017521900813ea524c9ecb8d7985ec26a9ad9a" integrity sha512-vvSaIDf/Ov0o3KwMT+1M8+WbnnlRiGjlGD5uvk83a1mPCTd/E5x12bUJ/oP55+wUY/4Kb5kc67rVpVGJ2KUHxg== +"@types/d3-interpolate@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz#1c280511f622de9b0b47d463fa55f9a4fd6f5fc8" + integrity sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ== + dependencies: + "@types/d3-color" "*" + "@types/d3-path@*", "@types/d3-path@^1.0.8": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.8.tgz#48e6945a8ff43ee0a1ce85c8cfa2337de85c7c79" @@ -5219,6 +5231,11 @@ d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0: resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== +"d3-array@1.2.0 - 2": + version "2.4.0" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.4.0.tgz#87f8b9ad11088769c82b5ea846bcb1cc9393f242" + integrity sha512-KQ41bAF2BMakf/HdKT865ALd4cgND6VcIztVQZUTt0+BH3RWy6ZYnHghVXf6NFjt2ritLr8H1T8LreAAlfiNcw== + d3-chord@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/d3-chord/-/d3-chord-1.0.6.tgz#309157e3f2db2c752f0280fedd35f2067ccbb15f" @@ -5254,7 +5271,7 @@ d3-hierarchy@^1.1.4, d3-hierarchy@^1.1.8: resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83" integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ== -d3-interpolate@1, d3-interpolate@^1.1.5: +d3-interpolate@1, d3-interpolate@^1.1.5, d3-interpolate@^1.2.0, d3-interpolate@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== @@ -5292,15 +5309,14 @@ d3-scale@^1.0.6: d3-time "1" d3-time-format "2" -d3-scale@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-2.2.2.tgz#4e880e0b2745acaaddd3ede26a9e908a9e17b81f" - integrity sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw== +d3-scale@^3.0.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.2.1.tgz#da1684adce7261b4bc7a76fe193d887f0e909e69" + integrity sha512-huz5byJO/6MPpz6Q8d4lg7GgSpTjIZW/l+1MQkzKfu2u8P6hjaXaStOpmyrD6ymKoW87d2QVFCKvSjLwjzx/rA== dependencies: - d3-array "^1.2.0" - d3-collection "1" + d3-array "1.2.0 - 2" d3-format "1" - d3-interpolate "1" + d3-interpolate "^1.2.0" d3-time "1" d3-time-format "2" From 26277e61ed8628baeffea1d5db3612688411e71e Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Fri, 17 Jul 2020 17:42:40 -0700 Subject: [PATCH 02/42] feat: add types and factory for all scales --- packages/vx-scale/src/mixins/applyZero.ts | 4 +- packages/vx-scale/src/scales/band.ts | 63 +++++++------------ packages/vx-scale/src/scales/ordinal.ts | 32 +++++----- packages/vx-scale/src/scales/point.ts | 45 ++++++------- packages/vx-scale/src/scales/quantile.ts | 28 ++++++--- packages/vx-scale/src/scales/quantize.ts | 43 ++++++------- packages/vx-scale/src/scales/symlog.ts | 36 ++++++----- packages/vx-scale/src/scales/threshold.ts | 29 +++++---- packages/vx-scale/src/scales/time.ts | 46 +++++++------- packages/vx-scale/src/scales/utc.ts | 46 +++++++------- packages/vx-scale/src/types/Base.ts | 3 + .../vx-scale/src/types/BaseScaleConfig.ts | 8 ++- packages/vx-scale/src/types/ScaleConfig.ts | 25 ++++---- 13 files changed, 202 insertions(+), 206 deletions(-) diff --git a/packages/vx-scale/src/mixins/applyZero.ts b/packages/vx-scale/src/mixins/applyZero.ts index c09f77c82..edfbe0536 100644 --- a/packages/vx-scale/src/mixins/applyZero.ts +++ b/packages/vx-scale/src/mixins/applyZero.ts @@ -1,8 +1,8 @@ import { Value } from '../types/Base'; -import { SomeD3Scale } from '../types/Scale'; +import { PickD3Scale } from '../types/Scale'; export default function applyZero( - scale: SomeD3Scale<'linear' | 'pow' | 'sqrt' | 'symlog', Output>, + scale: PickD3Scale<'linear' | 'pow' | 'sqrt' | 'symlog' | 'quantize', Output>, config: { zero?: boolean }, ) { if (config.zero === true) { diff --git a/packages/vx-scale/src/scales/band.ts b/packages/vx-scale/src/scales/band.ts index 34de5f8c4..cd2273e89 100644 --- a/packages/vx-scale/src/scales/band.ts +++ b/packages/vx-scale/src/scales/band.ts @@ -1,51 +1,32 @@ import { scaleBand } from 'd3-scale'; +import { Value, HasToString } from '../types/Base'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { PickD3Scale } from '../types/Scale'; +import applyRound from '../mixins/applyRound'; -type StringLike = string | { toString(): string }; -type Numeric = number | { valueOf(): number }; +export function updateBandScale( + scale: PickD3Scale<'band', Output>, + config: PickScaleConfigWithoutType<'band', Output>, +) { + const { align, domain, padding, paddingInner, paddingOuter, range } = config; -export type BandConfig = { - /** Sets the output values of the scale, which are numbers for band scales. */ - range?: [Numeric, Numeric]; - /** Sets the output values of the scale while setting its interpolator to round. If the elements are not numbers, they will be coerced to numbers. */ - rangeRound?: [Numeric, Numeric]; - /** Sets the input values of the scale, which are strings for band scales. */ - domain?: Datum[]; - /** 0-1, determines how any leftover unused space in the range is distributed. 0.5 distributes it equally left and right. */ - align?: number; - /** 0-1, determines the ratio of the range that is reserved for blank space before the first point and after the last. */ - padding?: number; - /** 0-1, determines the ratio of the range that is reserved for blank space _between_ bands. */ - paddingInner?: number; - /** 0-1, determines the ratio of the range that is reserved for blank space before the first band and after the last band. */ - paddingOuter?: number; - tickFormat?: unknown; -}; - -export default function bandScale({ - range, - rangeRound, - domain, - padding, - paddingInner, - paddingOuter, - align, - tickFormat, -}: BandConfig) { - const scale = scaleBand(); - - if (range) scale.range(range); - if (rangeRound) scale.rangeRound(rangeRound); if (domain) scale.domain(domain); - if (padding) scale.padding(padding); - if (paddingInner) scale.paddingInner(paddingInner); - if (paddingOuter) scale.paddingOuter(paddingOuter); - if (align) scale.align(align); + if (range) scale.range(range); + if (typeof padding !== 'undefined') scale.padding(padding); + if (typeof paddingInner !== 'undefined') scale.paddingInner(paddingInner); + if (typeof paddingOuter !== 'undefined') scale.paddingOuter(paddingOuter); + if (typeof align !== 'undefined') scale.align(align); + applyRound(scale, config); - // @TODO should likely get rid of these. - // @ts-ignore - if (tickFormat) scale.tickFormat = tickFormat; + // TODO: Remove? // @ts-ignore scale.type = 'band'; return scale; } + +export default function createBandScale( + config: PickScaleConfigWithoutType<'band', Output>, +) { + return updateBandScale(scaleBand(), config); +} diff --git a/packages/vx-scale/src/scales/ordinal.ts b/packages/vx-scale/src/scales/ordinal.ts index b190cda05..9bb96c93b 100644 --- a/packages/vx-scale/src/scales/ordinal.ts +++ b/packages/vx-scale/src/scales/ordinal.ts @@ -1,27 +1,27 @@ import { scaleOrdinal } from 'd3-scale'; +import { Value, HasToString } from '../types/Base'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { PickD3Scale } from '../types/Scale'; -export type OrdinalConfig = { - /** Sets the input values of the scale, which are strings for an ordinal scale. */ - domain?: Input[]; - /** Sets the output values of the scale. */ - range?: Output[]; - /** Sets the output value of the scale for unknown input values. */ - unknown?: Output | { name: 'implicit' }; -}; +export function updateOrdinalScale( + scale: PickD3Scale<'ordinal', Output>, + config: PickScaleConfigWithoutType<'ordinal', Output>, +) { + const { domain, range, unknown } = config; -export default function ordinalScale({ - range, - domain, - unknown, -}: OrdinalConfig) { - const scale = scaleOrdinal(); - - if (range) scale.range(range); if (domain) scale.domain(domain); + if (range) scale.range(range); if (unknown) scale.unknown(unknown); + // TODO: Remove? // @ts-ignore scale.type = 'ordinal'; return scale; } + +export default function createOrdinalScale( + config: PickScaleConfigWithoutType<'ordinal', Output>, +) { + return updateOrdinalScale(scaleOrdinal(), config); +} diff --git a/packages/vx-scale/src/scales/point.ts b/packages/vx-scale/src/scales/point.ts index 66fe6854a..212889c61 100644 --- a/packages/vx-scale/src/scales/point.ts +++ b/packages/vx-scale/src/scales/point.ts @@ -1,35 +1,30 @@ import { scalePoint } from 'd3-scale'; +import { Value, HasToString } from '../types/Base'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { PickD3Scale } from '../types/Scale'; +import applyRound from '../mixins/applyRound'; -export type PointConfig = { - /** Sets the output values of the scale, which are numbers for point scales. */ - range?: [number, number]; - /** Sets the output values of the scale while setting its interpolator to round. */ - rangeRound?: [number, number]; - /** Sets the input values of the scale. */ - domain?: Input[]; - /** 0-1, determines the ratio of the range that is reserved for blank space before the first point and after the last. */ - padding?: number; - /** 0-1, determines how any leftover unused space in the range is distributed. 0.5 distributes it equally left and right. */ - align?: number; -}; +export function updatePointScale( + scale: PickD3Scale<'point', Output>, + config: PickScaleConfigWithoutType<'point', Output>, +) { + const { align, domain, padding, range } = config; -export default function pointScale({ - range, - rangeRound, - domain, - padding, - align, -}: PointConfig) { - const scale = scalePoint(); - - if (range) scale.range(range); - if (rangeRound) scale.rangeRound(rangeRound); if (domain) scale.domain(domain); - if (padding) scale.padding(padding); - if (align) scale.align(align); + if (range) scale.range(range); + if (typeof padding !== 'undefined') scale.padding(padding); + if (typeof align !== 'undefined') scale.align(align); + applyRound(scale, config); + // TODO: Remove? // @ts-ignore scale.type = 'point'; return scale; } + +export default function createPointScale( + config: PickScaleConfigWithoutType<'point', Output>, +) { + return updatePointScale(scalePoint(), config); +} diff --git a/packages/vx-scale/src/scales/quantile.ts b/packages/vx-scale/src/scales/quantile.ts index 1bbcfb471..09f33b3aa 100644 --- a/packages/vx-scale/src/scales/quantile.ts +++ b/packages/vx-scale/src/scales/quantile.ts @@ -1,20 +1,28 @@ import { scaleQuantile } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickD3Scale } from '../types/Scale'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import applyInterpolate from '../mixins/applyInterpolate'; -export type QuantileConfig = { - /** Sets the output values of the scale. */ - range?: Output[]; - /** Sets the input values of the scale. */ - domain?: (number | null | undefined)[]; -}; +export function updateQuantileScale( + scale: PickD3Scale<'quantile', Output>, + config: PickScaleConfigWithoutType<'quantile', Output>, +) { + const { domain, range } = config; -export default function quantileScale({ range, domain }: QuantileConfig) { - const scale = scaleQuantile(); - - if (range) scale.range(range); if (domain) scale.domain(domain); + if (range) scale.range(range); + + applyInterpolate(scale, config); // @ts-ignore scale.type = 'quantile'; return scale; } + +export default function createQuantileScale( + config: PickScaleConfigWithoutType<'quantile', Output>, +) { + return updateQuantileScale(scaleQuantile(), config); +} diff --git a/packages/vx-scale/src/scales/quantize.ts b/packages/vx-scale/src/scales/quantize.ts index 25ef98975..0140ceb98 100644 --- a/packages/vx-scale/src/scales/quantize.ts +++ b/packages/vx-scale/src/scales/quantize.ts @@ -1,35 +1,30 @@ import { scaleQuantize } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { PickD3Scale } from '../types/Scale'; +import applyZero from '../mixins/applyZero'; -export type QuantizeConfig = { - /** Sets the output values of the scale, which are numbers for point scales. */ - range?: Output[]; - /** Sets the input values of the scale. */ - domain?: [number, number]; - /** Extends the domain so that it starts and ends on nice round values. */ - nice?: boolean; - /** Optional approximate number of ticks to be returned. */ - ticks?: number; - /** Specifies an approximate tick count and valid format specifier string. */ - tickFormat?: [number, string]; -}; +export function updateQuantizeScale( + scale: PickD3Scale<'quantize', Output>, + config: PickScaleConfigWithoutType<'quantize', Output>, +) { + const { domain, range, nice = true } = config; -export default function quantizeScale({ - range, - domain, - ticks, - tickFormat, - nice = false, -}: QuantizeConfig) { - const scale = scaleQuantize(); - - if (range) scale.range(range); if (domain) scale.domain(domain); if (nice) scale.nice(); - if (ticks) scale.ticks(ticks); - if (tickFormat) scale.tickFormat(...tickFormat); + if (range) scale.range(range); + applyZero(scale, config); + + // TODO: Remove? // @ts-ignore scale.type = 'quantize'; return scale; } + +export default function createQuantizeScale( + config: PickScaleConfigWithoutType<'quantize', Output>, +) { + return updateQuantizeScale(scaleQuantize(), config); +} diff --git a/packages/vx-scale/src/scales/symlog.ts b/packages/vx-scale/src/scales/symlog.ts index 921fa74ba..01f289363 100644 --- a/packages/vx-scale/src/scales/symlog.ts +++ b/packages/vx-scale/src/scales/symlog.ts @@ -1,24 +1,32 @@ -// @ts-ignore no type defs for symlog import { scaleSymlog } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickD3Scale } from '../types/Scale'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import applyRound from '../mixins/applyRound'; +import applyZero from '../mixins/applyZero'; -export type SymlogConfig = { - /** Sets the output values of the scale. */ - range?: any[]; - /** Sets the input values of the scale. */ - domain?: any[]; - /** Sets the symlog constant to the specified number, defaults to 1. */ - constant?: number; -}; +export function updateSymlogScale( + scale: PickD3Scale<'symlog', Output>, + config: PickScaleConfigWithoutType<'symlog', Output>, +) { + const { domain, range, clamp = true, nice = true } = config; -export default function symLogScale({ range, domain, constant }: SymlogConfig) { - const scale = scaleSymlog(); - - if (range) scale.range(range); if (domain) scale.domain(domain); - if (constant) scale.constant(constant); + if (nice) scale.nice(); + if (range) scale.range(range); + + scale.clamp(clamp); + applyRound(scale, config); + applyZero(scale, config); // @ts-ignore scale.type = 'symlog'; return scale; } + +export default function createSymlogScale( + config: PickScaleConfigWithoutType<'symlog', Output>, +) { + return updateSymlogScale(scaleSymlog(), config); +} diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index a5c4a399b..26e6226be 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -1,23 +1,26 @@ import { scaleThreshold } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { PickD3Scale } from '../types/Scale'; -export type ThresholdConfig = { - /** Sets the output values of the scale. */ - range?: Output[]; - /** Sets the input values of the scale. */ - domain?: Input[]; -}; +export function updateThresholdScale( + scale: PickD3Scale<'threshold', Output>, + config: PickScaleConfigWithoutType<'threshold', Output>, +) { + const { domain, range } = config; -export default function thresholdScale({ - range, - domain, -}: ThresholdConfig) { - const scale = scaleThreshold(); - - if (range) scale.range(range); if (domain) scale.domain(domain); + if (range) scale.range(range); + // TODO: Remove? // @ts-ignore scale.type = 'threshold'; return scale; } + +export default function createThresholdScale( + config: PickScaleConfigWithoutType<'threshold', Output>, +) { + return updateThresholdScale(scaleThreshold(), config); +} diff --git a/packages/vx-scale/src/scales/time.ts b/packages/vx-scale/src/scales/time.ts index 8ac26f819..61b6e7c97 100644 --- a/packages/vx-scale/src/scales/time.ts +++ b/packages/vx-scale/src/scales/time.ts @@ -1,35 +1,33 @@ import { scaleTime } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { PickD3Scale } from '../types/Scale'; +import applyInterpolate from '../mixins/applyInterpolate'; +import applyRound from '../mixins/applyRound'; -export type TimeConfig = { - /** Sets the input values of the scale, which are Dates or coercible to numbers for time scales. */ - domain?: (Date | number | { valueOf(): number })[]; - /** Sets the output values of the scale. */ - range?: Output[]; - /** Sets the output values of the scale while setting its interpolator to round. If the elements are not numbers, they will be coerced to numbers. */ - rangeRound?: number[]; - /** Extends the domain so that it starts and ends on nice round values. */ - nice?: boolean; - /** Whether the scale should clamp values to within the range. */ - clamp?: boolean; -}; +export function updateTimeScale( + scale: PickD3Scale<'time', Output>, + config: PickScaleConfigWithoutType<'time', Output>, +) { + const { domain, range, clamp = true, nice = true } = config; -export default function timeScale({ - range, - rangeRound, - domain, - nice = false, - clamp = false, -}: TimeConfig) { - const scale = scaleTime(); - - if (range) scale.range(range); - if (rangeRound) scale.rangeRound(rangeRound); if (domain) scale.domain(domain); if (nice) scale.nice(); - if (clamp) scale.clamp(true); + if (range) scale.range(range); + scale.clamp(clamp); + applyInterpolate(scale, config); + applyRound(scale, config); + + // TODO: Remove? // @ts-ignore scale.type = 'time'; return scale; } + +export default function createTimeScale( + config: PickScaleConfigWithoutType<'time', Output>, +) { + return updateTimeScale(scaleTime(), config); +} diff --git a/packages/vx-scale/src/scales/utc.ts b/packages/vx-scale/src/scales/utc.ts index 2b4b77735..e1a7a6805 100644 --- a/packages/vx-scale/src/scales/utc.ts +++ b/packages/vx-scale/src/scales/utc.ts @@ -1,35 +1,33 @@ import { scaleUtc } from 'd3-scale'; +import { Value } from '../types/Base'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { PickD3Scale } from '../types/Scale'; +import applyInterpolate from '../mixins/applyInterpolate'; +import applyRound from '../mixins/applyRound'; -export type UtcConfig = { - /** Sets the input values of the scale, which are Dates or coercible to numbers for UTC time scales. */ - domain?: (Date | number | { valueOf(): number })[]; - /** Sets the output values of the scale. */ - range?: Output[]; - /** Sets the output values of the scale while setting its interpolator to round. If the elements are not numbers, they will be coerced to numbers. */ - rangeRound?: number[]; - /** Extends the domain so that it starts and ends on nice round values. */ - nice?: boolean; - /** Whether the scale should clamp values to within the range. */ - clamp?: boolean; -}; +export function updateUtcScale( + scale: PickD3Scale<'utc', Output>, + config: PickScaleConfigWithoutType<'utc', Output>, +) { + const { domain, range, clamp = true, nice = true } = config; -export default function timeScale({ - range, - rangeRound, - domain, - nice = false, - clamp = false, -}: UtcConfig) { - const scale = scaleUtc(); - - if (range) scale.range(range); - if (rangeRound) scale.rangeRound(rangeRound); if (domain) scale.domain(domain); if (nice) scale.nice(); - if (clamp) scale.clamp(true); + if (range) scale.range(range); + scale.clamp(clamp); + applyInterpolate(scale, config); + applyRound(scale, config); + + // TODO: Remove? // @ts-ignore scale.type = 'utc'; return scale; } + +export default function createUtcScale( + config: PickScaleConfigWithoutType<'utc', Output>, +) { + return updateUtcScale(scaleUtc(), config); +} diff --git a/packages/vx-scale/src/types/Base.ts b/packages/vx-scale/src/types/Base.ts index 479716295..13768d83d 100644 --- a/packages/vx-scale/src/types/Base.ts +++ b/packages/vx-scale/src/types/Base.ts @@ -6,3 +6,6 @@ export type Value = number | string | boolean | null; /** Union types of all values from a map type */ export type ValueOf = T[keyof T]; + +/** Extract generic type from array */ +export type Unarray = T extends Array ? U : T; diff --git a/packages/vx-scale/src/types/BaseScaleConfig.ts b/packages/vx-scale/src/types/BaseScaleConfig.ts index 7ec562a32..a475bd159 100644 --- a/packages/vx-scale/src/types/BaseScaleConfig.ts +++ b/packages/vx-scale/src/types/BaseScaleConfig.ts @@ -1,3 +1,4 @@ +import { Unarray } from './Base'; import { ScaleInterpolate, ScaleInterpolateParams } from './ScaleInterpolate'; export interface BaseScaleConfig { @@ -119,6 +120,11 @@ export interface BaseScaleConfig { */ round?: boolean; + /** + * Sets the output value of the scale for unknown input values. + */ + unknown?: Unarray | { name: 'implicit' }; + /** * If `true`, ensures that a zero baseline value is included in the scale domain. * @@ -127,4 +133,4 @@ export interface BaseScaleConfig { * __Note:__ Log, time, and utc scales do not support `zero`. */ zero?: boolean; -}; +} diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index 54cbbb57c..dd22c75bc 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -2,7 +2,9 @@ import { BaseScaleConfig } from './BaseScaleConfig'; import { HasToString, Value, ValueOf } from './Base'; import { NiceTime } from './Nice'; -export type TimeInput = string | number | Date; +type Numeric = number | { valueOf(): number }; + +export type TimeInput = number | Date; export type ContinuousInput = number | Date; export type DiscreteInput = HasToString; @@ -13,7 +15,7 @@ export type ContinuousDomain = ContinuousInput[]; // from same base type to share property documentation // (which is useful for auto-complete/intellisense) // and add `type` property as discriminant of union type. -type CreateScaleConfig> = Pick< +type CreateScaleConfig = 'type'> = Pick< BaseScaleConfig, 'type' | 'domain' | 'range' | 'reverse' | Fields >; @@ -63,34 +65,33 @@ export type QuantileScaleConfig = CreateScaleConfi export type QuantizeScaleConfig = CreateScaleConfig< 'quantize', Output[], - ContinuousDomain, + [ContinuousInput, ContinuousInput], 'interpolate' | 'nice' | 'zero' >; export type ThresholdScaleConfig = CreateScaleConfig< 'threshold', Output[], - ContinuousDomain, - 'interpolate' | 'nice' + ContinuousDomain >; export type OrdinalScaleConfig = CreateScaleConfig< 'ordinal', Output[], DiscreteInput[], - 'interpolate' + 'unknown' >; -export type PointScaleConfig = CreateScaleConfig< +export type PointScaleConfig = CreateScaleConfig< 'point', - Output[], + [Numeric, Numeric], DiscreteInput[], 'align' | 'padding' | 'round' >; -export type BandScaleConfig = CreateScaleConfig< +export type BandScaleConfig = CreateScaleConfig< 'band', - Output[], + [Numeric, Numeric], DiscreteInput[], 'align' | 'padding' | 'paddingInner' | 'paddingOuter' | 'round' >; @@ -126,8 +127,8 @@ export interface ScaleTypeToScaleConfig { quantize: QuantizeScaleConfig; threshold: ThresholdScaleConfig; ordinal: OrdinalScaleConfig; - point: PointScaleConfig; - band: BandScaleConfig; + point: PointScaleConfig; + band: BandScaleConfig; } export type PickScaleConfig< From 7201124915b8e203006438ca3593bceb183cd71d Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Mon, 20 Jul 2020 18:42:03 -0700 Subject: [PATCH 03/42] fix: correctly type inputs --- packages/vx-scale/src/index.ts | 4 +- packages/vx-scale/src/mixins/applyRound.ts | 11 +-- packages/vx-scale/src/scales/band.ts | 20 +++-- packages/vx-scale/src/scales/ordinal.ts | 20 +++-- packages/vx-scale/src/scales/point.ts | 20 +++-- packages/vx-scale/src/scales/threshold.ts | 20 +++-- packages/vx-scale/src/scales/time.ts | 2 +- packages/vx-scale/src/types/Base.ts | 5 +- .../vx-scale/src/types/BaseScaleConfig.ts | 2 +- packages/vx-scale/src/types/Scale.ts | 43 +++++++-- packages/vx-scale/src/types/ScaleConfig.ts | 90 ++++++++++--------- 11 files changed, 146 insertions(+), 91 deletions(-) diff --git a/packages/vx-scale/src/index.ts b/packages/vx-scale/src/index.ts index d3b4fe895..e27d573a2 100644 --- a/packages/vx-scale/src/index.ts +++ b/packages/vx-scale/src/index.ts @@ -10,5 +10,7 @@ export { default as scaleQuantize } from './scales/quantize'; export { default as scaleQuantile } from './scales/quantile'; export { default as scaleSymlog } from './scales/symlog'; export { default as scaleThreshold } from './scales/threshold'; -export { default as updateScale } from './util/updateScale'; export { default as scaleSqrt } from './scales/squareRoot'; + +// Will change +export { default as updateScale } from './util/updateScale'; diff --git a/packages/vx-scale/src/mixins/applyRound.ts b/packages/vx-scale/src/mixins/applyRound.ts index da278f35e..35976bba7 100644 --- a/packages/vx-scale/src/mixins/applyRound.ts +++ b/packages/vx-scale/src/mixins/applyRound.ts @@ -1,12 +1,13 @@ import { interpolateRound } from 'd3-interpolate'; import { InterpolatorFactory } from 'd3-scale'; -import { Value } from '../types/Base'; +import { Value, StringLike } from '../types/Base'; import { D3Scale } from '../types/Scale'; -export default function applyRound( - scale: D3Scale, - config: { round?: boolean }, -) { +export default function applyRound< + Output extends Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends number | string | Date = number | string | Date +>(scale: D3Scale, config: { round?: boolean }) { if (typeof config.round !== 'undefined') { if ('round' in scale) { scale.round(config.round); diff --git a/packages/vx-scale/src/scales/band.ts b/packages/vx-scale/src/scales/band.ts index cd2273e89..d6d634e26 100644 --- a/packages/vx-scale/src/scales/band.ts +++ b/packages/vx-scale/src/scales/band.ts @@ -1,12 +1,15 @@ import { scaleBand } from 'd3-scale'; -import { Value, HasToString } from '../types/Base'; +import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { PickD3Scale } from '../types/Scale'; import applyRound from '../mixins/applyRound'; -export function updateBandScale( - scale: PickD3Scale<'band', Output>, - config: PickScaleConfigWithoutType<'band', Output>, +export function updateBandScale< + DiscreteInput extends StringLike = StringLike, + Output extends Value = Value +>( + scale: PickD3Scale<'band', Output, DiscreteInput>, + config: PickScaleConfigWithoutType<'band', Output, DiscreteInput>, ) { const { align, domain, padding, paddingInner, paddingOuter, range } = config; @@ -25,8 +28,9 @@ export function updateBandScale( return scale; } -export default function createBandScale( - config: PickScaleConfigWithoutType<'band', Output>, -) { - return updateBandScale(scaleBand(), config); +export default function createBandScale< + DiscreteInput extends StringLike = StringLike, + Output extends Value = Value +>(config: PickScaleConfigWithoutType<'band', Output, DiscreteInput>) { + return updateBandScale(scaleBand(), config); } diff --git a/packages/vx-scale/src/scales/ordinal.ts b/packages/vx-scale/src/scales/ordinal.ts index 9bb96c93b..a09b36e65 100644 --- a/packages/vx-scale/src/scales/ordinal.ts +++ b/packages/vx-scale/src/scales/ordinal.ts @@ -1,11 +1,14 @@ import { scaleOrdinal } from 'd3-scale'; -import { Value, HasToString } from '../types/Base'; +import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { PickD3Scale } from '../types/Scale'; -export function updateOrdinalScale( - scale: PickD3Scale<'ordinal', Output>, - config: PickScaleConfigWithoutType<'ordinal', Output>, +export function updateOrdinalScale< + DiscreteInput extends StringLike = StringLike, + Output extends Value = Value +>( + scale: PickD3Scale<'ordinal', Output, DiscreteInput>, + config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>, ) { const { domain, range, unknown } = config; @@ -20,8 +23,9 @@ export function updateOrdinalScale( return scale; } -export default function createOrdinalScale( - config: PickScaleConfigWithoutType<'ordinal', Output>, -) { - return updateOrdinalScale(scaleOrdinal(), config); +export default function createOrdinalScale< + DiscreteInput extends StringLike = StringLike, + Output extends Value = Value +>(config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>) { + return updateOrdinalScale(scaleOrdinal(), config); } diff --git a/packages/vx-scale/src/scales/point.ts b/packages/vx-scale/src/scales/point.ts index 212889c61..d71b40db4 100644 --- a/packages/vx-scale/src/scales/point.ts +++ b/packages/vx-scale/src/scales/point.ts @@ -1,12 +1,15 @@ import { scalePoint } from 'd3-scale'; -import { Value, HasToString } from '../types/Base'; +import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { PickD3Scale } from '../types/Scale'; import applyRound from '../mixins/applyRound'; -export function updatePointScale( - scale: PickD3Scale<'point', Output>, - config: PickScaleConfigWithoutType<'point', Output>, +export function updatePointScale< + DiscreteInput extends StringLike = StringLike, + Output extends Value = Value +>( + scale: PickD3Scale<'point', Output, DiscreteInput>, + config: PickScaleConfigWithoutType<'point', Output, DiscreteInput>, ) { const { align, domain, padding, range } = config; @@ -23,8 +26,9 @@ export function updatePointScale( return scale; } -export default function createPointScale( - config: PickScaleConfigWithoutType<'point', Output>, -) { - return updatePointScale(scalePoint(), config); +export default function createPointScale< + DiscreteInput extends StringLike = StringLike, + Output extends Value = Value +>(config: PickScaleConfigWithoutType<'point', Output, DiscreteInput>) { + return updatePointScale(scalePoint(), config); } diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index 26e6226be..5fb03d175 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -1,11 +1,14 @@ import { scaleThreshold } from 'd3-scale'; -import { Value } from '../types/Base'; +import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { PickD3Scale } from '../types/Scale'; -export function updateThresholdScale( - scale: PickD3Scale<'threshold', Output>, - config: PickScaleConfigWithoutType<'threshold', Output>, +export function updateThresholdScale< + ThresholdInput extends number | string | Date = number | string | Date, + Output extends Value = Value +>( + scale: PickD3Scale<'threshold', Output, StringLike, ThresholdInput>, + config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>, ) { const { domain, range } = config; @@ -19,8 +22,9 @@ export function updateThresholdScale( return scale; } -export default function createThresholdScale( - config: PickScaleConfigWithoutType<'threshold', Output>, -) { - return updateThresholdScale(scaleThreshold(), config); +export default function createThresholdScale< + ThresholdInput extends number | string | Date = number | string | Date, + Output extends Value = Value +>(config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>) { + return updateThresholdScale(scaleThreshold(), config); } diff --git a/packages/vx-scale/src/scales/time.ts b/packages/vx-scale/src/scales/time.ts index 61b6e7c97..f32b4a849 100644 --- a/packages/vx-scale/src/scales/time.ts +++ b/packages/vx-scale/src/scales/time.ts @@ -9,7 +9,7 @@ export function updateTimeScale( scale: PickD3Scale<'time', Output>, config: PickScaleConfigWithoutType<'time', Output>, ) { - const { domain, range, clamp = true, nice = true } = config; + const { domain, range, clamp = true, nice = false } = config; if (domain) scale.domain(domain); if (nice) scale.nice(); diff --git a/packages/vx-scale/src/types/Base.ts b/packages/vx-scale/src/types/Base.ts index 13768d83d..be4fa3bcf 100644 --- a/packages/vx-scale/src/types/Base.ts +++ b/packages/vx-scale/src/types/Base.ts @@ -1,5 +1,8 @@ +/** A value that has .valueOf() function */ +export type NumberLike = { valueOf(): number }; + /** A value that has .toString() function */ -export type HasToString = { toString(): string }; +export type StringLike = { toString(): string }; /** Possible values */ export type Value = number | string | boolean | null; diff --git a/packages/vx-scale/src/types/BaseScaleConfig.ts b/packages/vx-scale/src/types/BaseScaleConfig.ts index a475bd159..f190433b0 100644 --- a/packages/vx-scale/src/types/BaseScaleConfig.ts +++ b/packages/vx-scale/src/types/BaseScaleConfig.ts @@ -1,7 +1,7 @@ import { Unarray } from './Base'; import { ScaleInterpolate, ScaleInterpolateParams } from './ScaleInterpolate'; -export interface BaseScaleConfig { +export interface BaseScaleConfig { type: T; /** diff --git a/packages/vx-scale/src/types/Scale.ts b/packages/vx-scale/src/types/Scale.ts index 0d38edfac..3a516597e 100644 --- a/packages/vx-scale/src/types/Scale.ts +++ b/packages/vx-scale/src/types/Scale.ts @@ -11,27 +11,52 @@ import { ScaleBand, ScaleSymLog, } from 'd3-scale'; -import { HasToString, Value, ValueOf } from './Base'; +import { StringLike, Value, ValueOf } from './Base'; -export interface ScaleTypeToD3Scale { +/** + * Map scale type to D3Scale type + * @type `Output`: Output type of all scales except point and band + * @type `ThresholdInput`: Input type for threshold scale + * @type `DiscreteInput`: Input type for ordinal, point and band scales + */ +export interface ScaleTypeToD3Scale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends number | string | Date = number | string | Date +> { + // Input of these continuous scales are `number | { valueOf(): number }` + // and cannot be customized via generic type. linear: ScaleLinear; log: ScaleLogarithmic; pow: ScalePower; sqrt: ScalePower; symlog: ScaleSymLog; + // Input of time scales are `Date | number | { valueOf(): number }` + // and cannot be customized via generic type. time: ScaleTime; utc: ScaleTime; + // Input of these discretizing scales are `number | { valueOf(): number }` + // and cannot be customized via generic type. quantile: ScaleQuantile; quantize: ScaleQuantize; - threshold: ScaleThreshold; - ordinal: ScaleOrdinal; - point: ScalePoint; - band: ScaleBand; + // Threshold scale has its own Input generic type. + threshold: ScaleThreshold; + // Ordinal scale can customize both Input and Output types. + ordinal: ScaleOrdinal; + // Output of these two scales are always number while Input can be customized. + point: ScalePoint; + band: ScaleBand; } export type PickD3Scale< T extends keyof ScaleTypeToD3Scale, - Output extends Value = Value -> = ValueOf, T>>; + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends number | string | Date = number | string | Date +> = ValueOf, T>>; -export type D3Scale = ValueOf>; +export type D3Scale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends number | string | Date = number | string | Date +> = ValueOf>; diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index dd22c75bc..09aa63511 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -1,12 +1,11 @@ import { BaseScaleConfig } from './BaseScaleConfig'; -import { HasToString, Value, ValueOf } from './Base'; +import { StringLike, Value, ValueOf, NumberLike } from './Base'; import { NiceTime } from './Nice'; -type Numeric = number | { valueOf(): number }; +type Numeric = number | NumberLike; export type TimeInput = number | Date; export type ContinuousInput = number | Date; -export type DiscreteInput = HasToString; export type TimeDomain = TimeInput[]; export type ContinuousDomain = ContinuousInput[]; @@ -15,89 +14,86 @@ export type ContinuousDomain = ContinuousInput[]; // from same base type to share property documentation // (which is useful for auto-complete/intellisense) // and add `type` property as discriminant of union type. -type CreateScaleConfig = 'type'> = Pick< - BaseScaleConfig, +type CreateScaleConfig = 'type'> = Pick< + BaseScaleConfig, 'type' | 'domain' | 'range' | 'reverse' | Fields >; export type LinearScaleConfig = CreateScaleConfig< 'linear', - Output[], ContinuousDomain, + Output[], 'clamp' | 'interpolate' | 'nice' | 'round' | 'zero' >; export type LogScaleConfig = CreateScaleConfig< 'log', - Output[], ContinuousDomain, + Output[], 'base' | 'clamp' | 'interpolate' | 'nice' | 'round' >; export type PowScaleConfig = CreateScaleConfig< 'pow', - Output[], ContinuousDomain, + Output[], 'clamp' | 'exponent' | 'interpolate' | 'nice' | 'round' | 'zero' >; export type SqrtScaleConfig = CreateScaleConfig< 'sqrt', - Output[], ContinuousDomain, + Output[], 'clamp' | 'interpolate' | 'nice' | 'round' | 'zero' >; export type SymlogScaleConfig = CreateScaleConfig< 'symlog', - Output[], ContinuousDomain, + Output[], 'clamp' | 'constant' | 'nice' | 'round' | 'zero' >; export type QuantileScaleConfig = CreateScaleConfig< 'quantile', - Output[], ContinuousDomain, + Output[], 'interpolate' >; export type QuantizeScaleConfig = CreateScaleConfig< 'quantize', - Output[], [ContinuousInput, ContinuousInput], + Output[], 'interpolate' | 'nice' | 'zero' >; -export type ThresholdScaleConfig = CreateScaleConfig< - 'threshold', - Output[], - ContinuousDomain ->; +export type ThresholdScaleConfig< + ThresholdInput extends number | string | Date = number | string | Date, + Output extends Value = Value +> = CreateScaleConfig<'threshold', ThresholdInput[], Output[]>; -export type OrdinalScaleConfig = CreateScaleConfig< - 'ordinal', - Output[], - DiscreteInput[], - 'unknown' ->; +export type OrdinalScaleConfig< + DiscreteInput extends StringLike = StringLike, + Output extends Value = Value +> = CreateScaleConfig<'ordinal', DiscreteInput[], Output[], 'unknown'>; -export type PointScaleConfig = CreateScaleConfig< +export type PointScaleConfig = CreateScaleConfig< 'point', - [Numeric, Numeric], DiscreteInput[], + [Numeric, Numeric], 'align' | 'padding' | 'round' >; -export type BandScaleConfig = CreateScaleConfig< +export type BandScaleConfig = CreateScaleConfig< 'band', - [Numeric, Numeric], DiscreteInput[], + [Numeric, Numeric], 'align' | 'padding' | 'paddingInner' | 'paddingOuter' | 'round' >; interface TemporalScaleConfig - extends CreateScaleConfig { + extends CreateScaleConfig { /** * Extending the domain so that it starts and ends on nice round values. This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. Nicing is useful if the domain is computed from data and may be irregular. For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2, 1.0]_. * @@ -115,7 +111,11 @@ export type TimeScaleConfig = TemporalScaleConfig< export type UtcScaleConfig = TemporalScaleConfig<'utc', Output>; -export interface ScaleTypeToScaleConfig { +export interface ScaleTypeToScaleConfig< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends number | string | Date = number | string | Date +> { linear: LinearScaleConfig; log: LogScaleConfig; pow: PowScaleConfig; @@ -125,22 +125,30 @@ export interface ScaleTypeToScaleConfig { utc: UtcScaleConfig; quantile: QuantileScaleConfig; quantize: QuantizeScaleConfig; - threshold: ThresholdScaleConfig; - ordinal: OrdinalScaleConfig; - point: PointScaleConfig; - band: BandScaleConfig; + threshold: ThresholdScaleConfig; + ordinal: OrdinalScaleConfig; + point: PointScaleConfig; + band: BandScaleConfig; } export type PickScaleConfig< - T extends keyof ScaleTypeToScaleConfig, - Output extends Value = Value -> = ValueOf, T>>; + T extends keyof ScaleTypeToScaleConfig, + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends number | string | Date = number | string | Date +> = ValueOf, T>>; export type PickScaleConfigWithoutType< - T extends keyof ScaleTypeToScaleConfig, - Output extends Value = Value -> = Omit, 'type'>; - -export type ScaleConfig = ValueOf>; + T extends keyof ScaleTypeToScaleConfig, + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends number | string | Date = number | string | Date +> = Omit, 'type'>; + +export type ScaleConfig< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends number | string | Date = number | string | Date +> = ValueOf>; export type ScaleType = keyof ScaleTypeToScaleConfig; From cc311e191b3c70b7ca12eb755b2e13717617cfa8 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 21 Jul 2020 10:17:08 -0700 Subject: [PATCH 04/42] refactor: update input types and add overload --- packages/vx-scale/src/createScale.ts | 166 ++++++++++++++++++ packages/vx-scale/src/scales/band.ts | 20 +-- packages/vx-scale/src/scales/linear.ts | 12 +- packages/vx-scale/src/scales/log.ts | 12 +- packages/vx-scale/src/scales/ordinal.ts | 10 +- packages/vx-scale/src/scales/point.ts | 20 +-- packages/vx-scale/src/scales/power.ts | 12 +- packages/vx-scale/src/scales/quantile.ts | 10 +- packages/vx-scale/src/scales/quantize.ts | 12 +- packages/vx-scale/src/scales/squareRoot.ts | 12 +- packages/vx-scale/src/scales/symlog.ts | 12 +- packages/vx-scale/src/scales/threshold.ts | 10 +- packages/vx-scale/src/scales/time.ts | 12 +- packages/vx-scale/src/scales/utc.ts | 12 +- .../vx-scale/src/types/BaseScaleConfig.ts | 14 -- packages/vx-scale/src/types/ScaleConfig.ts | 14 +- 16 files changed, 255 insertions(+), 105 deletions(-) create mode 100644 packages/vx-scale/src/createScale.ts diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts new file mode 100644 index 000000000..645833e80 --- /dev/null +++ b/packages/vx-scale/src/createScale.ts @@ -0,0 +1,166 @@ +import { ScaleConfig, ScaleTypeToScaleConfig } from './types/ScaleConfig'; +import { StringLike, Value } from './types/Base'; +import createLinearScale from './scales/linear'; +import createLogScale from './scales/log'; +import createPowScale from './scales/power'; +import createSqrtScale from './scales/squareRoot'; +import createSymlogScale from './scales/symlog'; +import createTimeScale from './scales/time'; +import createUtcScale from './scales/utc'; +import createQuantileScale from './scales/quantile'; +import createQuantizeScale from './scales/quantize'; +import createThresholdScale from './scales/threshold'; +import createOrdinalScale from './scales/ordinal'; +import createPointScale from './scales/point'; +import createBandScale from './scales/band'; +import { ScaleTypeToD3Scale } from './types/Scale'; + +// Overload function for more strict typing, e.g., +// If the config is a linear config then a ScaleLinear will be returned +// instead of a union type of all scales. + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['linear'], +): ScaleTypeToD3Scale['linear']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['log'], +): ScaleTypeToD3Scale['log']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['pow'], +): ScaleTypeToD3Scale['pow']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['sqrt'], +): ScaleTypeToD3Scale['sqrt']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['sqrt'], +): ScaleTypeToD3Scale['symlog']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['time'], +): ScaleTypeToD3Scale['time']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['utc'], +): ScaleTypeToD3Scale['utc']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['quantile'], +): ScaleTypeToD3Scale['quantile']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['quantize'], +): ScaleTypeToD3Scale['quantize']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['threshold'], +): ScaleTypeToD3Scale['threshold']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['ordinal'], +): ScaleTypeToD3Scale['ordinal']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['point'], +): ScaleTypeToD3Scale['point']; + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: ScaleTypeToScaleConfig['band'], +): ScaleTypeToD3Scale['band']; + +// Actual implementation + +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>(config: ScaleConfig) { + switch (config.type) { + case 'linear': + return createLinearScale(config); + case 'log': + return createLogScale(config); + case 'pow': + return createPowScale(config); + case 'sqrt': + return createSqrtScale(config); + case 'symlog': + return createSymlogScale(config); + case 'time': + return createTimeScale(config); + case 'utc': + return createUtcScale(config); + case 'quantile': + return createQuantileScale(config); + case 'quantize': + return createQuantizeScale(config); + case 'threshold': + return createThresholdScale(config); + case 'ordinal': + return createOrdinalScale(config); + case 'point': + return createPointScale(config); + case 'band': + return createBandScale(config); + default: + // @ts-ignore + throw new Error(`Unknown scale type: ${config.type}`); + } +} + +export default createScale; diff --git a/packages/vx-scale/src/scales/band.ts b/packages/vx-scale/src/scales/band.ts index d6d634e26..51d6b10bf 100644 --- a/packages/vx-scale/src/scales/band.ts +++ b/packages/vx-scale/src/scales/band.ts @@ -1,15 +1,12 @@ import { scaleBand } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { PickD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; import applyRound from '../mixins/applyRound'; -export function updateBandScale< - DiscreteInput extends StringLike = StringLike, - Output extends Value = Value ->( - scale: PickD3Scale<'band', Output, DiscreteInput>, - config: PickScaleConfigWithoutType<'band', Output, DiscreteInput>, +export function updateBandScale( + scale: ScaleTypeToD3Scale['band'], + config: ScaleTypeToScaleConfig['band'], ) { const { align, domain, padding, paddingInner, paddingOuter, range } = config; @@ -28,9 +25,8 @@ export function updateBandScale< return scale; } -export default function createBandScale< - DiscreteInput extends StringLike = StringLike, - Output extends Value = Value ->(config: PickScaleConfigWithoutType<'band', Output, DiscreteInput>) { +export default function createBandScale( + config: ScaleTypeToScaleConfig['band'], +) { return updateBandScale(scaleBand(), config); } diff --git a/packages/vx-scale/src/scales/linear.ts b/packages/vx-scale/src/scales/linear.ts index 449692d06..5f5720b4b 100644 --- a/packages/vx-scale/src/scales/linear.ts +++ b/packages/vx-scale/src/scales/linear.ts @@ -1,16 +1,16 @@ import { scaleLinear } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { PickD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; export function updateLinearScale( - scale: PickD3Scale<'linear', Output>, - config: PickScaleConfigWithoutType<'linear', Output>, + scale: ScaleTypeToD3Scale['linear'], + config: ScaleTypeToScaleConfig['linear'], ) { - const { domain, range, clamp = true, nice = true } = config; + const { domain, range, clamp = false, nice = false } = config; if (domain) scale.domain(domain); if (nice) scale.nice(); @@ -29,7 +29,7 @@ export function updateLinearScale( } export default function createLinearScale( - config: PickScaleConfigWithoutType<'linear', Output>, + config: ScaleTypeToScaleConfig['linear'], ) { return updateLinearScale(scaleLinear(), config); } diff --git a/packages/vx-scale/src/scales/log.ts b/packages/vx-scale/src/scales/log.ts index 15fe1cf23..f69579673 100644 --- a/packages/vx-scale/src/scales/log.ts +++ b/packages/vx-scale/src/scales/log.ts @@ -1,15 +1,15 @@ import { scaleLog } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickD3Scale } from '../types/Scale'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; export function updateLogScale( - scale: PickD3Scale<'log', Output>, - config: PickScaleConfigWithoutType<'log', Output>, + scale: ScaleTypeToD3Scale['log'], + config: ScaleTypeToScaleConfig['log'], ) { - const { domain, range, base, clamp = true, nice = true } = config; + const { domain, range, base, clamp = false, nice = false } = config; if (base) scale.base(base); if (domain) scale.domain(domain); @@ -27,7 +27,7 @@ export function updateLogScale( } export default function createLogScale( - config: PickScaleConfigWithoutType<'log', Output>, + config: ScaleTypeToScaleConfig['log'], ) { return updateLogScale(scaleLog(), config); } diff --git a/packages/vx-scale/src/scales/ordinal.ts b/packages/vx-scale/src/scales/ordinal.ts index a09b36e65..c2a808d7c 100644 --- a/packages/vx-scale/src/scales/ordinal.ts +++ b/packages/vx-scale/src/scales/ordinal.ts @@ -1,14 +1,14 @@ import { scaleOrdinal } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { PickD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; export function updateOrdinalScale< DiscreteInput extends StringLike = StringLike, Output extends Value = Value >( - scale: PickD3Scale<'ordinal', Output, DiscreteInput>, - config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>, + scale: ScaleTypeToD3Scale['ordinal'], + config: ScaleTypeToScaleConfig['ordinal'], ) { const { domain, range, unknown } = config; @@ -26,6 +26,6 @@ export function updateOrdinalScale< export default function createOrdinalScale< DiscreteInput extends StringLike = StringLike, Output extends Value = Value ->(config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>) { +>(config: ScaleTypeToScaleConfig['ordinal']) { return updateOrdinalScale(scaleOrdinal(), config); } diff --git a/packages/vx-scale/src/scales/point.ts b/packages/vx-scale/src/scales/point.ts index d71b40db4..ba68892d4 100644 --- a/packages/vx-scale/src/scales/point.ts +++ b/packages/vx-scale/src/scales/point.ts @@ -1,15 +1,12 @@ import { scalePoint } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { PickD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; import applyRound from '../mixins/applyRound'; -export function updatePointScale< - DiscreteInput extends StringLike = StringLike, - Output extends Value = Value ->( - scale: PickD3Scale<'point', Output, DiscreteInput>, - config: PickScaleConfigWithoutType<'point', Output, DiscreteInput>, +export function updatePointScale( + scale: ScaleTypeToD3Scale['point'], + config: ScaleTypeToScaleConfig['point'], ) { const { align, domain, padding, range } = config; @@ -26,9 +23,8 @@ export function updatePointScale< return scale; } -export default function createPointScale< - DiscreteInput extends StringLike = StringLike, - Output extends Value = Value ->(config: PickScaleConfigWithoutType<'point', Output, DiscreteInput>) { +export default function createPointScale( + config: ScaleTypeToScaleConfig['point'], +) { return updatePointScale(scalePoint(), config); } diff --git a/packages/vx-scale/src/scales/power.ts b/packages/vx-scale/src/scales/power.ts index 9d718b41d..39305ca34 100644 --- a/packages/vx-scale/src/scales/power.ts +++ b/packages/vx-scale/src/scales/power.ts @@ -1,16 +1,16 @@ import { scalePow } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickD3Scale } from '../types/Scale'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; export function updatePowScale( - scale: PickD3Scale<'pow', Output>, - config: PickScaleConfigWithoutType<'pow', Output>, + scale: ScaleTypeToD3Scale['pow'], + config: ScaleTypeToScaleConfig['pow'], ) { - const { domain, range, clamp = true, exponent, nice = true } = config; + const { domain, range, clamp = false, exponent, nice = false } = config; if (domain) scale.domain(domain); if (nice) scale.nice(); @@ -29,7 +29,7 @@ export function updatePowScale( } export default function createPowScale( - config: PickScaleConfigWithoutType<'pow', Output>, + config: ScaleTypeToScaleConfig['pow'], ) { return updatePowScale(scalePow(), config); } diff --git a/packages/vx-scale/src/scales/quantile.ts b/packages/vx-scale/src/scales/quantile.ts index 09f33b3aa..6cd84080c 100644 --- a/packages/vx-scale/src/scales/quantile.ts +++ b/packages/vx-scale/src/scales/quantile.ts @@ -1,12 +1,12 @@ import { scaleQuantile } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickD3Scale } from '../types/Scale'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; export function updateQuantileScale( - scale: PickD3Scale<'quantile', Output>, - config: PickScaleConfigWithoutType<'quantile', Output>, + scale: ScaleTypeToD3Scale['quantile'], + config: ScaleTypeToScaleConfig['quantile'], ) { const { domain, range } = config; @@ -22,7 +22,7 @@ export function updateQuantileScale( } export default function createQuantileScale( - config: PickScaleConfigWithoutType<'quantile', Output>, + config: ScaleTypeToScaleConfig['quantile'], ) { return updateQuantileScale(scaleQuantile(), config); } diff --git a/packages/vx-scale/src/scales/quantize.ts b/packages/vx-scale/src/scales/quantize.ts index 0140ceb98..b7fd6bef5 100644 --- a/packages/vx-scale/src/scales/quantize.ts +++ b/packages/vx-scale/src/scales/quantize.ts @@ -1,14 +1,14 @@ import { scaleQuantize } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { PickD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; import applyZero from '../mixins/applyZero'; export function updateQuantizeScale( - scale: PickD3Scale<'quantize', Output>, - config: PickScaleConfigWithoutType<'quantize', Output>, + scale: ScaleTypeToD3Scale['quantize'], + config: ScaleTypeToScaleConfig['quantize'], ) { - const { domain, range, nice = true } = config; + const { domain, range, nice = false } = config; if (domain) scale.domain(domain); if (nice) scale.nice(); @@ -24,7 +24,7 @@ export function updateQuantizeScale( } export default function createQuantizeScale( - config: PickScaleConfigWithoutType<'quantize', Output>, + config: ScaleTypeToScaleConfig['quantize'], ) { return updateQuantizeScale(scaleQuantize(), config); } diff --git a/packages/vx-scale/src/scales/squareRoot.ts b/packages/vx-scale/src/scales/squareRoot.ts index 498c95be8..d2cb93d15 100644 --- a/packages/vx-scale/src/scales/squareRoot.ts +++ b/packages/vx-scale/src/scales/squareRoot.ts @@ -1,16 +1,16 @@ import { scaleSqrt } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickD3Scale } from '../types/Scale'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; export function updateSqrtScale( - scale: PickD3Scale<'sqrt', Output>, - config: PickScaleConfigWithoutType<'sqrt', Output>, + scale: ScaleTypeToD3Scale['sqrt'], + config: ScaleTypeToScaleConfig['sqrt'], ) { - const { domain, range, clamp = true, nice = true } = config; + const { domain, range, clamp = false, nice = false } = config; if (domain) scale.domain(domain); if (nice) scale.nice(); @@ -28,7 +28,7 @@ export function updateSqrtScale( } export default function createSqrtScale( - config: PickScaleConfigWithoutType<'sqrt', Output>, + config: ScaleTypeToScaleConfig['sqrt'], ) { return updateSqrtScale(scaleSqrt(), config); } diff --git a/packages/vx-scale/src/scales/symlog.ts b/packages/vx-scale/src/scales/symlog.ts index 01f289363..9de0ad20b 100644 --- a/packages/vx-scale/src/scales/symlog.ts +++ b/packages/vx-scale/src/scales/symlog.ts @@ -1,15 +1,15 @@ import { scaleSymlog } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickD3Scale } from '../types/Scale'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; export function updateSymlogScale( - scale: PickD3Scale<'symlog', Output>, - config: PickScaleConfigWithoutType<'symlog', Output>, + scale: ScaleTypeToD3Scale['symlog'], + config: ScaleTypeToScaleConfig['symlog'], ) { - const { domain, range, clamp = true, nice = true } = config; + const { domain, range, clamp = false, nice = false } = config; if (domain) scale.domain(domain); if (nice) scale.nice(); @@ -26,7 +26,7 @@ export function updateSymlogScale( } export default function createSymlogScale( - config: PickScaleConfigWithoutType<'symlog', Output>, + config: ScaleTypeToScaleConfig['symlog'], ) { return updateSymlogScale(scaleSymlog(), config); } diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index 5fb03d175..be25b1cdd 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -1,14 +1,14 @@ import { scaleThreshold } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { PickD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; export function updateThresholdScale< ThresholdInput extends number | string | Date = number | string | Date, Output extends Value = Value >( - scale: PickD3Scale<'threshold', Output, StringLike, ThresholdInput>, - config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>, + scale: ScaleTypeToD3Scale['threshold'], + config: ScaleTypeToScaleConfig['threshold'], ) { const { domain, range } = config; @@ -25,6 +25,6 @@ export function updateThresholdScale< export default function createThresholdScale< ThresholdInput extends number | string | Date = number | string | Date, Output extends Value = Value ->(config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>) { +>(config: ScaleTypeToScaleConfig['threshold']) { return updateThresholdScale(scaleThreshold(), config); } diff --git a/packages/vx-scale/src/scales/time.ts b/packages/vx-scale/src/scales/time.ts index f32b4a849..150cc3fa7 100644 --- a/packages/vx-scale/src/scales/time.ts +++ b/packages/vx-scale/src/scales/time.ts @@ -1,15 +1,15 @@ import { scaleTime } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { PickD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; export function updateTimeScale( - scale: PickD3Scale<'time', Output>, - config: PickScaleConfigWithoutType<'time', Output>, + scale: ScaleTypeToD3Scale['time'], + config: ScaleTypeToScaleConfig['time'], ) { - const { domain, range, clamp = true, nice = false } = config; + const { domain, range, clamp = false, nice = false } = config; if (domain) scale.domain(domain); if (nice) scale.nice(); @@ -27,7 +27,7 @@ export function updateTimeScale( } export default function createTimeScale( - config: PickScaleConfigWithoutType<'time', Output>, + config: ScaleTypeToScaleConfig['time'], ) { return updateTimeScale(scaleTime(), config); } diff --git a/packages/vx-scale/src/scales/utc.ts b/packages/vx-scale/src/scales/utc.ts index e1a7a6805..58098539a 100644 --- a/packages/vx-scale/src/scales/utc.ts +++ b/packages/vx-scale/src/scales/utc.ts @@ -1,15 +1,15 @@ import { scaleUtc } from 'd3-scale'; import { Value } from '../types/Base'; -import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { PickD3Scale } from '../types/Scale'; +import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { ScaleTypeToD3Scale } from '../types/Scale'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; export function updateUtcScale( - scale: PickD3Scale<'utc', Output>, - config: PickScaleConfigWithoutType<'utc', Output>, + scale: ScaleTypeToD3Scale['utc'], + config: ScaleTypeToScaleConfig['utc'], ) { - const { domain, range, clamp = true, nice = true } = config; + const { domain, range, clamp = false, nice = false } = config; if (domain) scale.domain(domain); if (nice) scale.nice(); @@ -27,7 +27,7 @@ export function updateUtcScale( } export default function createUtcScale( - config: PickScaleConfigWithoutType<'utc', Output>, + config: ScaleTypeToScaleConfig['utc'], ) { return updateUtcScale(scaleUtc(), config); } diff --git a/packages/vx-scale/src/types/BaseScaleConfig.ts b/packages/vx-scale/src/types/BaseScaleConfig.ts index f190433b0..caa9c273b 100644 --- a/packages/vx-scale/src/types/BaseScaleConfig.ts +++ b/packages/vx-scale/src/types/BaseScaleConfig.ts @@ -67,15 +67,6 @@ export interface BaseScaleConfig { nice?: boolean | number; /** - * For _[continuous](https://vega.github.io/vega-lite/docs/scale.html#continuous)_ scales, expands the scale domain to accommodate the specified number of pixels on each of the scale range. The scale range must represent pixels for this parameter to function as intended. - * Padding adjustment is performed prior to all other adjustments, including the effects of the `zero`, `nice` properties. - * - * For _[band](https://vega.github.io/vega-lite/docs/scale.html#band)_ scales, shortcut for setting `paddingInner` and `paddingOuter` to the same value. - * - * For _[point](https://vega.github.io/vega-lite/docs/scale.html#point)_ scales, alias for `paddingOuter`. - * - * __Default value:__ For _continuous_ scales, derived from the [scale config](https://vega.github.io/vega-lite/docs/scale.html#config)'s `continuousPadding`. - * For _band and point_ scales, see `paddingInner` and `paddingOuter`. By default, Vega-Lite sets padding such that _width/height = number of unique values * step_. * * @minimum 0 */ @@ -86,8 +77,6 @@ export interface BaseScaleConfig { * * For point scale, this property is invalid as point scales do not have internal band widths (only step sizes between bands). * - * __Default value:__ derived from the [scale config](https://vega.github.io/vega-lite/docs/scale.html#config)'s `bandPaddingInner`. - * * @minimum 0 * @maximum 1 */ @@ -97,9 +86,6 @@ export interface BaseScaleConfig { * The outer padding (spacing) at the ends of the range of band and point scales, * as a fraction of the step size. This value must lie in the range [0,1]. * - * __Default value:__ derived from the [scale config](https://vega.github.io/vega-lite/docs/scale.html#config)'s `bandPaddingOuter` for band scales and `pointPadding` for point scales. - * By default, Vega-Lite sets outer padding such that _width/height = number of unique values * step_. - * * @minimum 0 * @maximum 1 */ diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index 09aa63511..a824e8e14 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -111,6 +111,12 @@ export type TimeScaleConfig = TemporalScaleConfig< export type UtcScaleConfig = TemporalScaleConfig<'utc', Output>; +/** + * Map scale type to D3Scale type + * @type `Output`: Output type of all scales except point and band + * @type `ThresholdInput`: Input type for threshold scale + * @type `DiscreteInput`: Input type for ordinal, point and band scales + */ export interface ScaleTypeToScaleConfig< Output extends Value = Value, DiscreteInput extends StringLike = StringLike, @@ -131,15 +137,17 @@ export interface ScaleTypeToScaleConfig< band: BandScaleConfig; } +export type ScaleType = keyof ScaleTypeToScaleConfig; + export type PickScaleConfig< - T extends keyof ScaleTypeToScaleConfig, + T extends ScaleType, Output extends Value = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends number | string | Date = number | string | Date > = ValueOf, T>>; export type PickScaleConfigWithoutType< - T extends keyof ScaleTypeToScaleConfig, + T extends ScaleType, Output extends Value = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends number | string | Date = number | string | Date @@ -150,5 +158,3 @@ export type ScaleConfig< DiscreteInput extends StringLike = StringLike, ThresholdInput extends number | string | Date = number | string | Date > = ValueOf>; - -export type ScaleType = keyof ScaleTypeToScaleConfig; From a2e83f002690a04a210e5fbdff3640bcff4c0e32 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 21 Jul 2020 10:37:32 -0700 Subject: [PATCH 05/42] fix: fallback to linear scale --- packages/vx-scale/src/createScale.ts | 79 +++++++++++++++++----------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 645833e80..bc976ef50 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -27,6 +27,14 @@ function createScale< config: ScaleTypeToScaleConfig['linear'], ): ScaleTypeToD3Scale['linear']; +function createScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends number | string | Date +>( + config: Omit['linear'], 'type'>, +): ScaleTypeToD3Scale['linear']; + function createScale< Output extends Value, DiscreteInput extends StringLike, @@ -129,38 +137,47 @@ function createScale< Output extends Value, DiscreteInput extends StringLike, ThresholdInput extends number | string | Date ->(config: ScaleConfig) { - switch (config.type) { - case 'linear': - return createLinearScale(config); - case 'log': - return createLogScale(config); - case 'pow': - return createPowScale(config); - case 'sqrt': - return createSqrtScale(config); - case 'symlog': - return createSymlogScale(config); - case 'time': - return createTimeScale(config); - case 'utc': - return createUtcScale(config); - case 'quantile': - return createQuantileScale(config); - case 'quantize': - return createQuantizeScale(config); - case 'threshold': - return createThresholdScale(config); - case 'ordinal': - return createOrdinalScale(config); - case 'point': - return createPointScale(config); - case 'band': - return createBandScale(config); - default: - // @ts-ignore - throw new Error(`Unknown scale type: ${config.type}`); +>( + config: + | ScaleConfig + | Omit['linear'], 'type'>, +) { + if ('type' in config) { + switch (config.type) { + case 'linear': + return createLinearScale(config); + case 'log': + return createLogScale(config); + case 'pow': + return createPowScale(config); + case 'sqrt': + return createSqrtScale(config); + case 'symlog': + return createSymlogScale(config); + case 'time': + return createTimeScale(config); + case 'utc': + return createUtcScale(config); + case 'quantile': + return createQuantileScale(config); + case 'quantize': + return createQuantizeScale(config); + case 'threshold': + return createThresholdScale(config); + case 'ordinal': + return createOrdinalScale(config); + case 'point': + return createPointScale(config); + case 'band': + return createBandScale(config); + default: + // @ts-ignore + throw new Error(`Invalid scale type: ${config.type}`); + } } + + // If type is not specified, fallback to linear scale + return createLinearScale({ ...config, type: 'linear' }); } export default createScale; From 707e87b1a443795aa77bd02e271a6f84d11953d5 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 21 Jul 2020 10:38:56 -0700 Subject: [PATCH 06/42] fix: docs --- packages/vx-scale/src/types/BaseScaleConfig.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/vx-scale/src/types/BaseScaleConfig.ts b/packages/vx-scale/src/types/BaseScaleConfig.ts index caa9c273b..ad499858f 100644 --- a/packages/vx-scale/src/types/BaseScaleConfig.ts +++ b/packages/vx-scale/src/types/BaseScaleConfig.ts @@ -31,7 +31,7 @@ export interface BaseScaleConfig { /** * If `true`, values that exceed the data domain are clamped to either the minimum or maximum range value * - * __Default value:__ `true`. + * __Default value:__ `false`. */ clamp?: boolean; @@ -52,7 +52,6 @@ export interface BaseScaleConfig { * By default, a general interpolator for numbers, dates, strings and colors (in HCL space) is used. * For color ranges, this property allows interpolation in alternative color spaces. Legal values include `rgb`, `hsl`, `hsl-long`, `lab`, `hcl`, `hcl-long`, `cubehelix` and `cubehelix-long` ('-long' variants use longer paths in polar coordinate spaces). If object-valued, this property accepts an object with a string-valued _type_ property and an optional numeric _gamma_ property applicable to rgb and cubehelix interpolators. For more, see the [d3-interpolate documentation](https://github.com/d3/d3-interpolate). * - * * __Default value:__ `hcl` */ interpolate?: ScaleInterpolate | ScaleInterpolateParams; @@ -114,7 +113,7 @@ export interface BaseScaleConfig { /** * If `true`, ensures that a zero baseline value is included in the scale domain. * - * __Default value:__ `true` for x and y channels if the quantitative field is not binned and no custom `domain` is provided; `false` otherwise. + * __Default value:__ `false` * * __Note:__ Log, time, and utc scales do not support `zero`. */ From 81408f37458a5e5e1efb4fb852b1b549c6f31b4a Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 21 Jul 2020 10:40:10 -0700 Subject: [PATCH 07/42] fix: overload --- packages/vx-scale/src/createScale.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index bc976ef50..85c8fed98 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -24,15 +24,9 @@ function createScale< DiscreteInput extends StringLike, ThresholdInput extends number | string | Date >( - config: ScaleTypeToScaleConfig['linear'], -): ScaleTypeToD3Scale['linear']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date ->( - config: Omit['linear'], 'type'>, + config: + | ScaleTypeToScaleConfig['linear'] + | Omit['linear'], 'type'>, ): ScaleTypeToD3Scale['linear']; function createScale< From a7ab8370aea437e77a5fc4f096d2d680dca594fa Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 21 Jul 2020 14:17:53 -0700 Subject: [PATCH 08/42] refactor: add DefaultThresholdInput --- packages/vx-scale/src/createScale.ts | 30 +++++++++++----------- packages/vx-scale/src/mixins/applyRound.ts | 4 +-- packages/vx-scale/src/scales/threshold.ts | 6 ++--- packages/vx-scale/src/types/Scale.ts | 8 +++--- packages/vx-scale/src/types/ScaleConfig.ts | 11 ++++---- 5 files changed, 31 insertions(+), 28 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 85c8fed98..1d1ce1568 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -13,7 +13,7 @@ import createThresholdScale from './scales/threshold'; import createOrdinalScale from './scales/ordinal'; import createPointScale from './scales/point'; import createBandScale from './scales/band'; -import { ScaleTypeToD3Scale } from './types/Scale'; +import { ScaleTypeToD3Scale, DefaultThresholdInput } from './types/Scale'; // Overload function for more strict typing, e.g., // If the config is a linear config then a ScaleLinear will be returned @@ -22,7 +22,7 @@ import { ScaleTypeToD3Scale } from './types/Scale'; function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: | ScaleTypeToScaleConfig['linear'] @@ -32,7 +32,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['log'], ): ScaleTypeToD3Scale['log']; @@ -40,7 +40,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['pow'], ): ScaleTypeToD3Scale['pow']; @@ -48,7 +48,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['sqrt'], ): ScaleTypeToD3Scale['sqrt']; @@ -56,7 +56,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['sqrt'], ): ScaleTypeToD3Scale['symlog']; @@ -64,7 +64,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['time'], ): ScaleTypeToD3Scale['time']; @@ -72,7 +72,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['utc'], ): ScaleTypeToD3Scale['utc']; @@ -80,7 +80,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['quantile'], ): ScaleTypeToD3Scale['quantile']; @@ -88,7 +88,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['quantize'], ): ScaleTypeToD3Scale['quantize']; @@ -96,7 +96,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['threshold'], ): ScaleTypeToD3Scale['threshold']; @@ -104,7 +104,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['ordinal'], ): ScaleTypeToD3Scale['ordinal']; @@ -112,7 +112,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['point'], ): ScaleTypeToD3Scale['point']; @@ -120,7 +120,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: ScaleTypeToScaleConfig['band'], ): ScaleTypeToD3Scale['band']; @@ -130,7 +130,7 @@ function createScale< function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends number | string | Date + ThresholdInput extends DefaultThresholdInput >( config: | ScaleConfig diff --git a/packages/vx-scale/src/mixins/applyRound.ts b/packages/vx-scale/src/mixins/applyRound.ts index 35976bba7..72d765c03 100644 --- a/packages/vx-scale/src/mixins/applyRound.ts +++ b/packages/vx-scale/src/mixins/applyRound.ts @@ -1,12 +1,12 @@ import { interpolateRound } from 'd3-interpolate'; import { InterpolatorFactory } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { D3Scale } from '../types/Scale'; +import { D3Scale, DefaultThresholdInput } from '../types/Scale'; export default function applyRound< Output extends Value, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends number | string | Date = number | string | Date + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >(scale: D3Scale, config: { round?: boolean }) { if (typeof config.round !== 'undefined') { if ('round' in scale) { diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index be25b1cdd..a4e79e0ba 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -1,10 +1,10 @@ import { scaleThreshold } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale } from '../types/Scale'; +import { ScaleTypeToD3Scale, DefaultThresholdInput } from '../types/Scale'; export function updateThresholdScale< - ThresholdInput extends number | string | Date = number | string | Date, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Output extends Value = Value >( scale: ScaleTypeToD3Scale['threshold'], @@ -23,7 +23,7 @@ export function updateThresholdScale< } export default function createThresholdScale< - ThresholdInput extends number | string | Date = number | string | Date, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Output extends Value = Value >(config: ScaleTypeToScaleConfig['threshold']) { return updateThresholdScale(scaleThreshold(), config); diff --git a/packages/vx-scale/src/types/Scale.ts b/packages/vx-scale/src/types/Scale.ts index 3a516597e..7c185b9b2 100644 --- a/packages/vx-scale/src/types/Scale.ts +++ b/packages/vx-scale/src/types/Scale.ts @@ -13,6 +13,8 @@ import { } from 'd3-scale'; import { StringLike, Value, ValueOf } from './Base'; +export type DefaultThresholdInput = number | string | Date; + /** * Map scale type to D3Scale type * @type `Output`: Output type of all scales except point and band @@ -22,7 +24,7 @@ import { StringLike, Value, ValueOf } from './Base'; export interface ScaleTypeToD3Scale< Output extends Value = Value, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends number | string | Date = number | string | Date + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > { // Input of these continuous scales are `number | { valueOf(): number }` // and cannot be customized via generic type. @@ -52,11 +54,11 @@ export type PickD3Scale< T extends keyof ScaleTypeToD3Scale, Output extends Value = Value, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends number | string | Date = number | string | Date + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf, T>>; export type D3Scale< Output extends Value = Value, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends number | string | Date = number | string | Date + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>; diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index a824e8e14..3a93dfabc 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -1,6 +1,7 @@ import { BaseScaleConfig } from './BaseScaleConfig'; import { StringLike, Value, ValueOf, NumberLike } from './Base'; import { NiceTime } from './Nice'; +import { DefaultThresholdInput } from './Scale'; type Numeric = number | NumberLike; @@ -69,7 +70,7 @@ export type QuantizeScaleConfig = CreateScaleConfi >; export type ThresholdScaleConfig< - ThresholdInput extends number | string | Date = number | string | Date, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Output extends Value = Value > = CreateScaleConfig<'threshold', ThresholdInput[], Output[]>; @@ -120,7 +121,7 @@ export type UtcScaleConfig = TemporalScaleConfig<' export interface ScaleTypeToScaleConfig< Output extends Value = Value, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends number | string | Date = number | string | Date + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > { linear: LinearScaleConfig; log: LogScaleConfig; @@ -143,18 +144,18 @@ export type PickScaleConfig< T extends ScaleType, Output extends Value = Value, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends number | string | Date = number | string | Date + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf, T>>; export type PickScaleConfigWithoutType< T extends ScaleType, Output extends Value = Value, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends number | string | Date = number | string | Date + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = Omit, 'type'>; export type ScaleConfig< Output extends Value = Value, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends number | string | Date = number | string | Date + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>; From c52affc01796cad711148eda554c54a0ffc70c64 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 21 Jul 2020 17:01:20 -0700 Subject: [PATCH 09/42] fix: generic type --- packages/vx-scale/src/index.ts | 1 + .../vx-scale/src/mixins/applyInterpolate.ts | 2 +- packages/vx-scale/src/mixins/applyRound.ts | 2 +- packages/vx-scale/src/mixins/applyZero.ts | 2 +- packages/vx-scale/src/scales/linear.ts | 4 +-- packages/vx-scale/src/scales/log.ts | 4 +-- packages/vx-scale/src/scales/ordinal.ts | 7 ++-- packages/vx-scale/src/scales/power.ts | 4 +-- packages/vx-scale/src/scales/quantile.ts | 4 +-- packages/vx-scale/src/scales/quantize.ts | 4 +-- packages/vx-scale/src/scales/squareRoot.ts | 4 +-- packages/vx-scale/src/scales/symlog.ts | 4 +-- packages/vx-scale/src/scales/threshold.ts | 4 +-- packages/vx-scale/src/scales/time.ts | 4 +-- packages/vx-scale/src/scales/utc.ts | 4 +-- packages/vx-scale/src/types/Scale.ts | 6 ++-- packages/vx-scale/src/types/ScaleConfig.ts | 32 +++++++++---------- 17 files changed, 45 insertions(+), 47 deletions(-) diff --git a/packages/vx-scale/src/index.ts b/packages/vx-scale/src/index.ts index e27d573a2..9d870961a 100644 --- a/packages/vx-scale/src/index.ts +++ b/packages/vx-scale/src/index.ts @@ -12,5 +12,6 @@ export { default as scaleSymlog } from './scales/symlog'; export { default as scaleThreshold } from './scales/threshold'; export { default as scaleSqrt } from './scales/squareRoot'; +export { default as scale } from './createScale'; // Will change export { default as updateScale } from './util/updateScale'; diff --git a/packages/vx-scale/src/mixins/applyInterpolate.ts b/packages/vx-scale/src/mixins/applyInterpolate.ts index 6b14018ea..0f888271b 100644 --- a/packages/vx-scale/src/mixins/applyInterpolate.ts +++ b/packages/vx-scale/src/mixins/applyInterpolate.ts @@ -4,7 +4,7 @@ import { D3Scale } from '../types/Scale'; import { ScaleInterpolate, ScaleInterpolateParams } from '../types/ScaleInterpolate'; import createColorInterpolator from '../util/createColorInterpolator'; -export default function applyInterpolate( +export default function applyInterpolate( scale: D3Scale, config: { interpolate?: ScaleInterpolate | ScaleInterpolateParams }, ) { diff --git a/packages/vx-scale/src/mixins/applyRound.ts b/packages/vx-scale/src/mixins/applyRound.ts index 72d765c03..6a3b58745 100644 --- a/packages/vx-scale/src/mixins/applyRound.ts +++ b/packages/vx-scale/src/mixins/applyRound.ts @@ -4,7 +4,7 @@ import { Value, StringLike } from '../types/Base'; import { D3Scale, DefaultThresholdInput } from '../types/Scale'; export default function applyRound< - Output extends Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >(scale: D3Scale, config: { round?: boolean }) { diff --git a/packages/vx-scale/src/mixins/applyZero.ts b/packages/vx-scale/src/mixins/applyZero.ts index edfbe0536..d7a28cb0f 100644 --- a/packages/vx-scale/src/mixins/applyZero.ts +++ b/packages/vx-scale/src/mixins/applyZero.ts @@ -1,7 +1,7 @@ import { Value } from '../types/Base'; import { PickD3Scale } from '../types/Scale'; -export default function applyZero( +export default function applyZero( scale: PickD3Scale<'linear' | 'pow' | 'sqrt' | 'symlog' | 'quantize', Output>, config: { zero?: boolean }, ) { diff --git a/packages/vx-scale/src/scales/linear.ts b/packages/vx-scale/src/scales/linear.ts index 5f5720b4b..d86033d27 100644 --- a/packages/vx-scale/src/scales/linear.ts +++ b/packages/vx-scale/src/scales/linear.ts @@ -6,7 +6,7 @@ import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; -export function updateLinearScale( +export function updateLinearScale( scale: ScaleTypeToD3Scale['linear'], config: ScaleTypeToScaleConfig['linear'], ) { @@ -28,7 +28,7 @@ export function updateLinearScale( return scale; } -export default function createLinearScale( +export default function createLinearScale( config: ScaleTypeToScaleConfig['linear'], ) { return updateLinearScale(scaleLinear(), config); diff --git a/packages/vx-scale/src/scales/log.ts b/packages/vx-scale/src/scales/log.ts index f69579673..df5e98815 100644 --- a/packages/vx-scale/src/scales/log.ts +++ b/packages/vx-scale/src/scales/log.ts @@ -5,7 +5,7 @@ import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; -export function updateLogScale( +export function updateLogScale( scale: ScaleTypeToD3Scale['log'], config: ScaleTypeToScaleConfig['log'], ) { @@ -26,7 +26,7 @@ export function updateLogScale( return scale; } -export default function createLogScale( +export default function createLogScale( config: ScaleTypeToScaleConfig['log'], ) { return updateLogScale(scaleLog(), config); diff --git a/packages/vx-scale/src/scales/ordinal.ts b/packages/vx-scale/src/scales/ordinal.ts index c2a808d7c..b147e3242 100644 --- a/packages/vx-scale/src/scales/ordinal.ts +++ b/packages/vx-scale/src/scales/ordinal.ts @@ -3,10 +3,7 @@ import { Value, StringLike } from '../types/Base'; import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale } from '../types/Scale'; -export function updateOrdinalScale< - DiscreteInput extends StringLike = StringLike, - Output extends Value = Value ->( +export function updateOrdinalScale( scale: ScaleTypeToD3Scale['ordinal'], config: ScaleTypeToScaleConfig['ordinal'], ) { @@ -25,7 +22,7 @@ export function updateOrdinalScale< export default function createOrdinalScale< DiscreteInput extends StringLike = StringLike, - Output extends Value = Value + Output = Value >(config: ScaleTypeToScaleConfig['ordinal']) { return updateOrdinalScale(scaleOrdinal(), config); } diff --git a/packages/vx-scale/src/scales/power.ts b/packages/vx-scale/src/scales/power.ts index 39305ca34..99b74aedc 100644 --- a/packages/vx-scale/src/scales/power.ts +++ b/packages/vx-scale/src/scales/power.ts @@ -6,7 +6,7 @@ import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; -export function updatePowScale( +export function updatePowScale( scale: ScaleTypeToD3Scale['pow'], config: ScaleTypeToScaleConfig['pow'], ) { @@ -28,7 +28,7 @@ export function updatePowScale( return scale; } -export default function createPowScale( +export default function createPowScale( config: ScaleTypeToScaleConfig['pow'], ) { return updatePowScale(scalePow(), config); diff --git a/packages/vx-scale/src/scales/quantile.ts b/packages/vx-scale/src/scales/quantile.ts index 6cd84080c..028f89589 100644 --- a/packages/vx-scale/src/scales/quantile.ts +++ b/packages/vx-scale/src/scales/quantile.ts @@ -4,7 +4,7 @@ import { ScaleTypeToD3Scale } from '../types/Scale'; import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; -export function updateQuantileScale( +export function updateQuantileScale( scale: ScaleTypeToD3Scale['quantile'], config: ScaleTypeToScaleConfig['quantile'], ) { @@ -21,7 +21,7 @@ export function updateQuantileScale( return scale; } -export default function createQuantileScale( +export default function createQuantileScale( config: ScaleTypeToScaleConfig['quantile'], ) { return updateQuantileScale(scaleQuantile(), config); diff --git a/packages/vx-scale/src/scales/quantize.ts b/packages/vx-scale/src/scales/quantize.ts index b7fd6bef5..9f1fedd7b 100644 --- a/packages/vx-scale/src/scales/quantize.ts +++ b/packages/vx-scale/src/scales/quantize.ts @@ -4,7 +4,7 @@ import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale } from '../types/Scale'; import applyZero from '../mixins/applyZero'; -export function updateQuantizeScale( +export function updateQuantizeScale( scale: ScaleTypeToD3Scale['quantize'], config: ScaleTypeToScaleConfig['quantize'], ) { @@ -23,7 +23,7 @@ export function updateQuantizeScale( return scale; } -export default function createQuantizeScale( +export default function createQuantizeScale( config: ScaleTypeToScaleConfig['quantize'], ) { return updateQuantizeScale(scaleQuantize(), config); diff --git a/packages/vx-scale/src/scales/squareRoot.ts b/packages/vx-scale/src/scales/squareRoot.ts index d2cb93d15..60568bf31 100644 --- a/packages/vx-scale/src/scales/squareRoot.ts +++ b/packages/vx-scale/src/scales/squareRoot.ts @@ -6,7 +6,7 @@ import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; -export function updateSqrtScale( +export function updateSqrtScale( scale: ScaleTypeToD3Scale['sqrt'], config: ScaleTypeToScaleConfig['sqrt'], ) { @@ -27,7 +27,7 @@ export function updateSqrtScale( return scale; } -export default function createSqrtScale( +export default function createSqrtScale( config: ScaleTypeToScaleConfig['sqrt'], ) { return updateSqrtScale(scaleSqrt(), config); diff --git a/packages/vx-scale/src/scales/symlog.ts b/packages/vx-scale/src/scales/symlog.ts index 9de0ad20b..85cef33b7 100644 --- a/packages/vx-scale/src/scales/symlog.ts +++ b/packages/vx-scale/src/scales/symlog.ts @@ -5,7 +5,7 @@ import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; -export function updateSymlogScale( +export function updateSymlogScale( scale: ScaleTypeToD3Scale['symlog'], config: ScaleTypeToScaleConfig['symlog'], ) { @@ -25,7 +25,7 @@ export function updateSymlogScale( return scale; } -export default function createSymlogScale( +export default function createSymlogScale( config: ScaleTypeToScaleConfig['symlog'], ) { return updateSymlogScale(scaleSymlog(), config); diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index a4e79e0ba..5d441b4bf 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -5,7 +5,7 @@ import { ScaleTypeToD3Scale, DefaultThresholdInput } from '../types/Scale'; export function updateThresholdScale< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, - Output extends Value = Value + Output = Value >( scale: ScaleTypeToD3Scale['threshold'], config: ScaleTypeToScaleConfig['threshold'], @@ -24,7 +24,7 @@ export function updateThresholdScale< export default function createThresholdScale< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, - Output extends Value = Value + Output = Value >(config: ScaleTypeToScaleConfig['threshold']) { return updateThresholdScale(scaleThreshold(), config); } diff --git a/packages/vx-scale/src/scales/time.ts b/packages/vx-scale/src/scales/time.ts index 150cc3fa7..00edef390 100644 --- a/packages/vx-scale/src/scales/time.ts +++ b/packages/vx-scale/src/scales/time.ts @@ -5,7 +5,7 @@ import { ScaleTypeToD3Scale } from '../types/Scale'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; -export function updateTimeScale( +export function updateTimeScale( scale: ScaleTypeToD3Scale['time'], config: ScaleTypeToScaleConfig['time'], ) { @@ -26,7 +26,7 @@ export function updateTimeScale( return scale; } -export default function createTimeScale( +export default function createTimeScale( config: ScaleTypeToScaleConfig['time'], ) { return updateTimeScale(scaleTime(), config); diff --git a/packages/vx-scale/src/scales/utc.ts b/packages/vx-scale/src/scales/utc.ts index 58098539a..afe827a49 100644 --- a/packages/vx-scale/src/scales/utc.ts +++ b/packages/vx-scale/src/scales/utc.ts @@ -5,7 +5,7 @@ import { ScaleTypeToD3Scale } from '../types/Scale'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; -export function updateUtcScale( +export function updateUtcScale( scale: ScaleTypeToD3Scale['utc'], config: ScaleTypeToScaleConfig['utc'], ) { @@ -26,7 +26,7 @@ export function updateUtcScale( return scale; } -export default function createUtcScale( +export default function createUtcScale( config: ScaleTypeToScaleConfig['utc'], ) { return updateUtcScale(scaleUtc(), config); diff --git a/packages/vx-scale/src/types/Scale.ts b/packages/vx-scale/src/types/Scale.ts index 7c185b9b2..fc8c3baaf 100644 --- a/packages/vx-scale/src/types/Scale.ts +++ b/packages/vx-scale/src/types/Scale.ts @@ -22,7 +22,7 @@ export type DefaultThresholdInput = number | string | Date; * @type `DiscreteInput`: Input type for ordinal, point and band scales */ export interface ScaleTypeToD3Scale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > { @@ -52,13 +52,13 @@ export interface ScaleTypeToD3Scale< export type PickD3Scale< T extends keyof ScaleTypeToD3Scale, - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf, T>>; export type D3Scale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>; diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index 3a93dfabc..b6eaec9f0 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -20,49 +20,49 @@ type CreateScaleConfig = 'type' | 'domain' | 'range' | 'reverse' | Fields >; -export type LinearScaleConfig = CreateScaleConfig< +export type LinearScaleConfig = CreateScaleConfig< 'linear', ContinuousDomain, Output[], 'clamp' | 'interpolate' | 'nice' | 'round' | 'zero' >; -export type LogScaleConfig = CreateScaleConfig< +export type LogScaleConfig = CreateScaleConfig< 'log', ContinuousDomain, Output[], 'base' | 'clamp' | 'interpolate' | 'nice' | 'round' >; -export type PowScaleConfig = CreateScaleConfig< +export type PowScaleConfig = CreateScaleConfig< 'pow', ContinuousDomain, Output[], 'clamp' | 'exponent' | 'interpolate' | 'nice' | 'round' | 'zero' >; -export type SqrtScaleConfig = CreateScaleConfig< +export type SqrtScaleConfig = CreateScaleConfig< 'sqrt', ContinuousDomain, Output[], 'clamp' | 'interpolate' | 'nice' | 'round' | 'zero' >; -export type SymlogScaleConfig = CreateScaleConfig< +export type SymlogScaleConfig = CreateScaleConfig< 'symlog', ContinuousDomain, Output[], 'clamp' | 'constant' | 'nice' | 'round' | 'zero' >; -export type QuantileScaleConfig = CreateScaleConfig< +export type QuantileScaleConfig = CreateScaleConfig< 'quantile', ContinuousDomain, Output[], 'interpolate' >; -export type QuantizeScaleConfig = CreateScaleConfig< +export type QuantizeScaleConfig = CreateScaleConfig< 'quantize', [ContinuousInput, ContinuousInput], Output[], @@ -71,12 +71,12 @@ export type QuantizeScaleConfig = CreateScaleConfi export type ThresholdScaleConfig< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, - Output extends Value = Value + Output = Value > = CreateScaleConfig<'threshold', ThresholdInput[], Output[]>; export type OrdinalScaleConfig< DiscreteInput extends StringLike = StringLike, - Output extends Value = Value + Output = Value > = CreateScaleConfig<'ordinal', DiscreteInput[], Output[], 'unknown'>; export type PointScaleConfig = CreateScaleConfig< @@ -93,7 +93,7 @@ export type BandScaleConfig = Cre 'align' | 'padding' | 'paddingInner' | 'paddingOuter' | 'round' >; -interface TemporalScaleConfig +interface TemporalScaleConfig extends CreateScaleConfig { /** * Extending the domain so that it starts and ends on nice round values. This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. Nicing is useful if the domain is computed from data and may be irregular. For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2, 1.0]_. @@ -108,9 +108,9 @@ interface TemporalScaleConfig nice?: boolean | number | NiceTime | { interval: NiceTime; step: number }; } -export type TimeScaleConfig = TemporalScaleConfig<'time', Output>; +export type TimeScaleConfig = TemporalScaleConfig<'time', Output>; -export type UtcScaleConfig = TemporalScaleConfig<'utc', Output>; +export type UtcScaleConfig = TemporalScaleConfig<'utc', Output>; /** * Map scale type to D3Scale type @@ -119,7 +119,7 @@ export type UtcScaleConfig = TemporalScaleConfig<' * @type `DiscreteInput`: Input type for ordinal, point and band scales */ export interface ScaleTypeToScaleConfig< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > { @@ -142,20 +142,20 @@ export type ScaleType = keyof ScaleTypeToScaleConfig; export type PickScaleConfig< T extends ScaleType, - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf, T>>; export type PickScaleConfigWithoutType< T extends ScaleType, - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = Omit, 'type'>; export type ScaleConfig< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>; From 48e2c4a0cc040356dd23b44fb9038c0c05af51cf Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Tue, 21 Jul 2020 17:23:17 -0700 Subject: [PATCH 10/42] refactor: simplify overloading --- packages/vx-scale/src/createScale.ts | 105 ++------------------- packages/vx-scale/src/types/ScaleConfig.ts | 9 +- 2 files changed, 15 insertions(+), 99 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 1d1ce1568..cc2fbc01d 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -1,4 +1,4 @@ -import { ScaleConfig, ScaleTypeToScaleConfig } from './types/ScaleConfig'; +import { ScaleConfig, ScaleTypeToScaleConfig, ScaleConfigToD3Scale } from './types/ScaleConfig'; import { StringLike, Value } from './types/Base'; import createLinearScale from './scales/linear'; import createLogScale from './scales/log'; @@ -22,108 +22,17 @@ import { ScaleTypeToD3Scale, DefaultThresholdInput } from './types/Scale'; function createScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: - | ScaleTypeToScaleConfig['linear'] - | Omit['linear'], 'type'>, -): ScaleTypeToD3Scale['linear']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['log'], -): ScaleTypeToD3Scale['log']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['pow'], -): ScaleTypeToD3Scale['pow']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['sqrt'], -): ScaleTypeToD3Scale['sqrt']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['sqrt'], -): ScaleTypeToD3Scale['symlog']; + ThresholdInput extends DefaultThresholdInput, + Config extends ScaleConfig +>(config: Config): ScaleConfigToD3Scale; function createScale< Output extends Value, DiscreteInput extends StringLike, ThresholdInput extends DefaultThresholdInput >( - config: ScaleTypeToScaleConfig['time'], -): ScaleTypeToD3Scale['time']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['utc'], -): ScaleTypeToD3Scale['utc']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['quantile'], -): ScaleTypeToD3Scale['quantile']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['quantize'], -): ScaleTypeToD3Scale['quantize']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['threshold'], -): ScaleTypeToD3Scale['threshold']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['ordinal'], -): ScaleTypeToD3Scale['ordinal']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['point'], -): ScaleTypeToD3Scale['point']; - -function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput ->( - config: ScaleTypeToScaleConfig['band'], -): ScaleTypeToD3Scale['band']; + config: Omit['linear'], 'type'>, +): ScaleTypeToD3Scale['linear']; // Actual implementation @@ -134,7 +43,7 @@ function createScale< >( config: | ScaleConfig - | Omit['linear'], 'type'>, + | Omit['linear'], 'type'>, ) { if ('type' in config) { switch (config.type) { diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index b6eaec9f0..ef1d641db 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -1,7 +1,7 @@ import { BaseScaleConfig } from './BaseScaleConfig'; import { StringLike, Value, ValueOf, NumberLike } from './Base'; import { NiceTime } from './Nice'; -import { DefaultThresholdInput } from './Scale'; +import { DefaultThresholdInput, ScaleTypeToD3Scale } from './Scale'; type Numeric = number | NumberLike; @@ -159,3 +159,10 @@ export type ScaleConfig< DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>; + +export type ScaleConfigToD3Scale< + Config extends ScaleConfig, + Output = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +> = ScaleTypeToD3Scale[Config['type']]; From c5a718cc7237720bf26564900fb545e8a90d2a46 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 10:29:21 -0700 Subject: [PATCH 11/42] fix: omit type from config and remove scale type --- packages/vx-scale/src/createScale.ts | 4 +- packages/vx-scale/src/index.ts | 3 +- packages/vx-scale/src/scales/band.ts | 10 +-- packages/vx-scale/src/scales/linear.ts | 12 +-- packages/vx-scale/src/scales/log.ts | 9 +- packages/vx-scale/src/scales/ordinal.ts | 10 +-- packages/vx-scale/src/scales/point.ts | 10 +-- packages/vx-scale/src/scales/power.ts | 9 +- packages/vx-scale/src/scales/quantile.ts | 9 +- packages/vx-scale/src/scales/quantize.ts | 10 +-- packages/vx-scale/src/scales/squareRoot.ts | 9 +- packages/vx-scale/src/scales/symlog.ts | 9 +- packages/vx-scale/src/scales/threshold.ts | 10 +-- packages/vx-scale/src/scales/time.ts | 10 +-- packages/vx-scale/src/scales/utc.ts | 29 +----- packages/vx-scale/src/updateScale.ts | 100 +++++++++++++++++++++ 16 files changed, 144 insertions(+), 109 deletions(-) create mode 100644 packages/vx-scale/src/updateScale.ts diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index cc2fbc01d..668615a14 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -1,4 +1,5 @@ import { ScaleConfig, ScaleTypeToScaleConfig, ScaleConfigToD3Scale } from './types/ScaleConfig'; +import { ScaleTypeToD3Scale, DefaultThresholdInput } from './types/Scale'; import { StringLike, Value } from './types/Base'; import createLinearScale from './scales/linear'; import createLogScale from './scales/log'; @@ -13,7 +14,6 @@ import createThresholdScale from './scales/threshold'; import createOrdinalScale from './scales/ordinal'; import createPointScale from './scales/point'; import createBandScale from './scales/band'; -import { ScaleTypeToD3Scale, DefaultThresholdInput } from './types/Scale'; // Overload function for more strict typing, e.g., // If the config is a linear config then a ScaleLinear will be returned @@ -80,7 +80,7 @@ function createScale< } // If type is not specified, fallback to linear scale - return createLinearScale({ ...config, type: 'linear' }); + return createLinearScale(config); } export default createScale; diff --git a/packages/vx-scale/src/index.ts b/packages/vx-scale/src/index.ts index 9d870961a..b6ca39e54 100644 --- a/packages/vx-scale/src/index.ts +++ b/packages/vx-scale/src/index.ts @@ -13,5 +13,4 @@ export { default as scaleThreshold } from './scales/threshold'; export { default as scaleSqrt } from './scales/squareRoot'; export { default as scale } from './createScale'; -// Will change -export { default as updateScale } from './util/updateScale'; +export { default as updateScale } from './updateScale'; diff --git a/packages/vx-scale/src/scales/band.ts b/packages/vx-scale/src/scales/band.ts index 51d6b10bf..faac401c7 100644 --- a/packages/vx-scale/src/scales/band.ts +++ b/packages/vx-scale/src/scales/band.ts @@ -1,12 +1,12 @@ import { scaleBand } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale } from '../types/Scale'; import applyRound from '../mixins/applyRound'; export function updateBandScale( scale: ScaleTypeToD3Scale['band'], - config: ScaleTypeToScaleConfig['band'], + config: PickScaleConfigWithoutType<'band', Value, DiscreteInput>, ) { const { align, domain, padding, paddingInner, paddingOuter, range } = config; @@ -18,15 +18,11 @@ export function updateBandScale( if (typeof align !== 'undefined') scale.align(align); applyRound(scale, config); - // TODO: Remove? - // @ts-ignore - scale.type = 'band'; - return scale; } export default function createBandScale( - config: ScaleTypeToScaleConfig['band'], + config: PickScaleConfigWithoutType<'band', Value, DiscreteInput>, ) { return updateBandScale(scaleBand(), config); } diff --git a/packages/vx-scale/src/scales/linear.ts b/packages/vx-scale/src/scales/linear.ts index d86033d27..a5c1dad26 100644 --- a/packages/vx-scale/src/scales/linear.ts +++ b/packages/vx-scale/src/scales/linear.ts @@ -1,14 +1,14 @@ import { scaleLinear } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale } from '../types/Scale'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; +import { ScaleTypeToD3Scale } from '../types/Scale'; export function updateLinearScale( scale: ScaleTypeToD3Scale['linear'], - config: ScaleTypeToScaleConfig['linear'], + config: PickScaleConfigWithoutType<'linear', Output>, ) { const { domain, range, clamp = false, nice = false } = config; @@ -21,15 +21,11 @@ export function updateLinearScale( applyRound(scale, config); applyZero(scale, config); - // TODO: Remove? - // @ts-ignore - scale.type = 'linear'; - return scale; } export default function createLinearScale( - config: ScaleTypeToScaleConfig['linear'], + config: PickScaleConfigWithoutType<'linear', Output>, ) { return updateLinearScale(scaleLinear(), config); } diff --git a/packages/vx-scale/src/scales/log.ts b/packages/vx-scale/src/scales/log.ts index df5e98815..2a8b306b2 100644 --- a/packages/vx-scale/src/scales/log.ts +++ b/packages/vx-scale/src/scales/log.ts @@ -1,13 +1,13 @@ import { scaleLog } from 'd3-scale'; import { Value } from '../types/Base'; import { ScaleTypeToD3Scale } from '../types/Scale'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; export function updateLogScale( scale: ScaleTypeToD3Scale['log'], - config: ScaleTypeToScaleConfig['log'], + config: PickScaleConfigWithoutType<'log', Output>, ) { const { domain, range, base, clamp = false, nice = false } = config; @@ -20,14 +20,11 @@ export function updateLogScale( applyInterpolate(scale, config); applyRound(scale, config); - // @ts-ignore - scale.type = 'log'; - return scale; } export default function createLogScale( - config: ScaleTypeToScaleConfig['log'], + config: PickScaleConfigWithoutType<'log', Output>, ) { return updateLogScale(scaleLog(), config); } diff --git a/packages/vx-scale/src/scales/ordinal.ts b/packages/vx-scale/src/scales/ordinal.ts index b147e3242..b6f3fa9cb 100644 --- a/packages/vx-scale/src/scales/ordinal.ts +++ b/packages/vx-scale/src/scales/ordinal.ts @@ -1,11 +1,11 @@ import { scaleOrdinal } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale } from '../types/Scale'; export function updateOrdinalScale( scale: ScaleTypeToD3Scale['ordinal'], - config: ScaleTypeToScaleConfig['ordinal'], + config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>, ) { const { domain, range, unknown } = config; @@ -13,16 +13,12 @@ export function updateOrdinalScale(config: ScaleTypeToScaleConfig['ordinal']) { +>(config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>) { return updateOrdinalScale(scaleOrdinal(), config); } diff --git a/packages/vx-scale/src/scales/point.ts b/packages/vx-scale/src/scales/point.ts index ba68892d4..a43bed0b7 100644 --- a/packages/vx-scale/src/scales/point.ts +++ b/packages/vx-scale/src/scales/point.ts @@ -1,12 +1,12 @@ import { scalePoint } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale } from '../types/Scale'; import applyRound from '../mixins/applyRound'; export function updatePointScale( scale: ScaleTypeToD3Scale['point'], - config: ScaleTypeToScaleConfig['point'], + config: PickScaleConfigWithoutType<'point', Value, DiscreteInput>, ) { const { align, domain, padding, range } = config; @@ -16,15 +16,11 @@ export function updatePointScale( if (typeof align !== 'undefined') scale.align(align); applyRound(scale, config); - // TODO: Remove? - // @ts-ignore - scale.type = 'point'; - return scale; } export default function createPointScale( - config: ScaleTypeToScaleConfig['point'], + config: PickScaleConfigWithoutType<'point', Value, DiscreteInput>, ) { return updatePointScale(scalePoint(), config); } diff --git a/packages/vx-scale/src/scales/power.ts b/packages/vx-scale/src/scales/power.ts index 99b74aedc..2808c93ca 100644 --- a/packages/vx-scale/src/scales/power.ts +++ b/packages/vx-scale/src/scales/power.ts @@ -1,14 +1,14 @@ import { scalePow } from 'd3-scale'; import { Value } from '../types/Base'; import { ScaleTypeToD3Scale } from '../types/Scale'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; export function updatePowScale( scale: ScaleTypeToD3Scale['pow'], - config: ScaleTypeToScaleConfig['pow'], + config: PickScaleConfigWithoutType<'pow', Output>, ) { const { domain, range, clamp = false, exponent, nice = false } = config; @@ -22,14 +22,11 @@ export function updatePowScale( applyRound(scale, config); applyZero(scale, config); - // @ts-ignore - scale.type = 'power'; - return scale; } export default function createPowScale( - config: ScaleTypeToScaleConfig['pow'], + config: PickScaleConfigWithoutType<'pow', Output>, ) { return updatePowScale(scalePow(), config); } diff --git a/packages/vx-scale/src/scales/quantile.ts b/packages/vx-scale/src/scales/quantile.ts index 028f89589..49bdadb68 100644 --- a/packages/vx-scale/src/scales/quantile.ts +++ b/packages/vx-scale/src/scales/quantile.ts @@ -1,12 +1,12 @@ import { scaleQuantile } from 'd3-scale'; import { Value } from '../types/Base'; import { ScaleTypeToD3Scale } from '../types/Scale'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; export function updateQuantileScale( scale: ScaleTypeToD3Scale['quantile'], - config: ScaleTypeToScaleConfig['quantile'], + config: PickScaleConfigWithoutType<'quantile', Output>, ) { const { domain, range } = config; @@ -15,14 +15,11 @@ export function updateQuantileScale( applyInterpolate(scale, config); - // @ts-ignore - scale.type = 'quantile'; - return scale; } export default function createQuantileScale( - config: ScaleTypeToScaleConfig['quantile'], + config: PickScaleConfigWithoutType<'quantile', Output>, ) { return updateQuantileScale(scaleQuantile(), config); } diff --git a/packages/vx-scale/src/scales/quantize.ts b/packages/vx-scale/src/scales/quantize.ts index 9f1fedd7b..153d1922d 100644 --- a/packages/vx-scale/src/scales/quantize.ts +++ b/packages/vx-scale/src/scales/quantize.ts @@ -1,12 +1,12 @@ import { scaleQuantize } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale } from '../types/Scale'; import applyZero from '../mixins/applyZero'; export function updateQuantizeScale( scale: ScaleTypeToD3Scale['quantize'], - config: ScaleTypeToScaleConfig['quantize'], + config: PickScaleConfigWithoutType<'quantize', Output>, ) { const { domain, range, nice = false } = config; @@ -16,15 +16,11 @@ export function updateQuantizeScale( applyZero(scale, config); - // TODO: Remove? - // @ts-ignore - scale.type = 'quantize'; - return scale; } export default function createQuantizeScale( - config: ScaleTypeToScaleConfig['quantize'], + config: PickScaleConfigWithoutType<'quantize', Output>, ) { return updateQuantizeScale(scaleQuantize(), config); } diff --git a/packages/vx-scale/src/scales/squareRoot.ts b/packages/vx-scale/src/scales/squareRoot.ts index 60568bf31..594a9edb8 100644 --- a/packages/vx-scale/src/scales/squareRoot.ts +++ b/packages/vx-scale/src/scales/squareRoot.ts @@ -1,14 +1,14 @@ import { scaleSqrt } from 'd3-scale'; import { Value } from '../types/Base'; import { ScaleTypeToD3Scale } from '../types/Scale'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; export function updateSqrtScale( scale: ScaleTypeToD3Scale['sqrt'], - config: ScaleTypeToScaleConfig['sqrt'], + config: PickScaleConfigWithoutType<'sqrt', Output>, ) { const { domain, range, clamp = false, nice = false } = config; @@ -21,14 +21,11 @@ export function updateSqrtScale( applyRound(scale, config); applyZero(scale, config); - // @ts-ignore - scale.type = 'squareRoot'; - return scale; } export default function createSqrtScale( - config: ScaleTypeToScaleConfig['sqrt'], + config: PickScaleConfigWithoutType<'sqrt', Output>, ) { return updateSqrtScale(scaleSqrt(), config); } diff --git a/packages/vx-scale/src/scales/symlog.ts b/packages/vx-scale/src/scales/symlog.ts index 85cef33b7..98230eeb8 100644 --- a/packages/vx-scale/src/scales/symlog.ts +++ b/packages/vx-scale/src/scales/symlog.ts @@ -1,13 +1,13 @@ import { scaleSymlog } from 'd3-scale'; import { Value } from '../types/Base'; import { ScaleTypeToD3Scale } from '../types/Scale'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import applyRound from '../mixins/applyRound'; import applyZero from '../mixins/applyZero'; export function updateSymlogScale( scale: ScaleTypeToD3Scale['symlog'], - config: ScaleTypeToScaleConfig['symlog'], + config: PickScaleConfigWithoutType<'symlog', Output>, ) { const { domain, range, clamp = false, nice = false } = config; @@ -19,14 +19,11 @@ export function updateSymlogScale( applyRound(scale, config); applyZero(scale, config); - // @ts-ignore - scale.type = 'symlog'; - return scale; } export default function createSymlogScale( - config: ScaleTypeToScaleConfig['symlog'], + config: PickScaleConfigWithoutType<'symlog', Output>, ) { return updateSymlogScale(scaleSymlog(), config); } diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index 5d441b4bf..623d06ef0 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -1,6 +1,6 @@ import { scaleThreshold } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale, DefaultThresholdInput } from '../types/Scale'; export function updateThresholdScale< @@ -8,23 +8,19 @@ export function updateThresholdScale< Output = Value >( scale: ScaleTypeToD3Scale['threshold'], - config: ScaleTypeToScaleConfig['threshold'], + config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>, ) { const { domain, range } = config; if (domain) scale.domain(domain); if (range) scale.range(range); - // TODO: Remove? - // @ts-ignore - scale.type = 'threshold'; - return scale; } export default function createThresholdScale< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Output = Value ->(config: ScaleTypeToScaleConfig['threshold']) { +>(config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>) { return updateThresholdScale(scaleThreshold(), config); } diff --git a/packages/vx-scale/src/scales/time.ts b/packages/vx-scale/src/scales/time.ts index 00edef390..51b846a43 100644 --- a/packages/vx-scale/src/scales/time.ts +++ b/packages/vx-scale/src/scales/time.ts @@ -1,13 +1,13 @@ import { scaleTime } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale } from '../types/Scale'; import applyInterpolate from '../mixins/applyInterpolate'; import applyRound from '../mixins/applyRound'; export function updateTimeScale( scale: ScaleTypeToD3Scale['time'], - config: ScaleTypeToScaleConfig['time'], + config: PickScaleConfigWithoutType<'time', Output>, ) { const { domain, range, clamp = false, nice = false } = config; @@ -19,15 +19,11 @@ export function updateTimeScale( applyInterpolate(scale, config); applyRound(scale, config); - // TODO: Remove? - // @ts-ignore - scale.type = 'time'; - return scale; } export default function createTimeScale( - config: ScaleTypeToScaleConfig['time'], + config: PickScaleConfigWithoutType<'time', Output>, ) { return updateTimeScale(scaleTime(), config); } diff --git a/packages/vx-scale/src/scales/utc.ts b/packages/vx-scale/src/scales/utc.ts index afe827a49..2949c0994 100644 --- a/packages/vx-scale/src/scales/utc.ts +++ b/packages/vx-scale/src/scales/utc.ts @@ -1,33 +1,12 @@ import { scaleUtc } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToScaleConfig } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale } from '../types/Scale'; -import applyInterpolate from '../mixins/applyInterpolate'; -import applyRound from '../mixins/applyRound'; +import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { updateTimeScale } from './time'; -export function updateUtcScale( - scale: ScaleTypeToD3Scale['utc'], - config: ScaleTypeToScaleConfig['utc'], -) { - const { domain, range, clamp = false, nice = false } = config; - - if (domain) scale.domain(domain); - if (nice) scale.nice(); - if (range) scale.range(range); - - scale.clamp(clamp); - applyInterpolate(scale, config); - applyRound(scale, config); - - // TODO: Remove? - // @ts-ignore - scale.type = 'utc'; - - return scale; -} +export const updateUtcScale = updateTimeScale; export default function createUtcScale( - config: ScaleTypeToScaleConfig['utc'], + config: PickScaleConfigWithoutType<'utc', Output>, ) { return updateUtcScale(scaleUtc(), config); } diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts new file mode 100644 index 000000000..0308602f1 --- /dev/null +++ b/packages/vx-scale/src/updateScale.ts @@ -0,0 +1,100 @@ +import { + ScaleLinear, + ScaleLogarithmic, + ScalePower, + ScaleSymLog, + ScaleTime, + ScaleQuantile, + ScaleQuantize, + ScaleThreshold, + ScaleOrdinal, + ScalePoint, + ScaleBand, +} from 'd3-scale'; +import { ScaleConfig, ScaleConfigToD3Scale } from './types/ScaleConfig'; +import { DefaultThresholdInput, D3Scale } from './types/Scale'; +import { StringLike, Value } from './types/Base'; +import { updateLinearScale } from './scales/linear'; +import { updateLogScale } from './scales/log'; +import { updatePowScale } from './scales/power'; +import { updateSqrtScale } from './scales/squareRoot'; +import { updateSymlogScale } from './scales/symlog'; +import { updateTimeScale } from './scales/time'; +import { updateUtcScale } from './scales/utc'; +import { updateQuantileScale } from './scales/quantile'; +import { updateQuantizeScale } from './scales/quantize'; +import { updateThresholdScale } from './scales/threshold'; +import { updateOrdinalScale } from './scales/ordinal'; +import { updatePointScale } from './scales/point'; +import { updateBandScale } from './scales/band'; + +// Overload function for more strict typing, e.g., +// If the config is a linear config then a ScaleLinear will be returned +// instead of a union type of all scales. + +function updateScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput, + Config extends ScaleConfig +>( + scale: ScaleConfigToD3Scale, + config: Config, +): ScaleConfigToD3Scale; + +function updateScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput, + Scale extends D3Scale +>(scale: Scale, config?: undefined): Scale; + +// Actual implementation + +function updateScale< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput, + Config extends ScaleConfig +>( + scale: ScaleConfigToD3Scale, + config?: ScaleConfig, +) { + if (typeof config === 'undefined') { + return scale.copy(); + } + + switch (config.type) { + case 'linear': + return updateLinearScale(scale.copy() as ScaleLinear, config); + case 'log': + return updateLogScale(scale as ScaleLogarithmic, config); + case 'pow': + return updatePowScale(scale as ScalePower, config); + case 'sqrt': + return updateSqrtScale(scale as ScalePower, config); + case 'symlog': + return updateSymlogScale(scale as ScaleSymLog, config); + case 'time': + return updateTimeScale(scale as ScaleTime, config); + case 'utc': + return updateUtcScale(scale as ScaleTime, config); + case 'quantile': + return updateQuantileScale(scale as ScaleQuantile, config); + case 'quantize': + return updateQuantizeScale(scale as ScaleQuantize, config); + case 'threshold': + return updateThresholdScale(scale as ScaleThreshold, config); + case 'ordinal': + return updateOrdinalScale(scale as ScaleOrdinal, config); + case 'point': + return updatePointScale(scale as ScalePoint, config); + case 'band': + return updateBandScale(scale as ScaleBand, config); + default: + // @ts-ignore + throw new Error(`Invalid scale type: ${config.type}`); + } +} + +export default updateScale; From 21c738aa0f7479e884ba81618757b0a068e99470 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 13:55:13 -0700 Subject: [PATCH 12/42] refactor: update scale --- packages/vx-scale/src/scales/band.ts | 10 +- packages/vx-scale/src/scales/quantile.ts | 3 - packages/vx-scale/src/scales/squareRoot.ts | 17 +- packages/vx-scale/src/types/ScaleConfig.ts | 11 +- packages/vx-scale/src/updateScale.ts | 230 ++++++++++++++------- 5 files changed, 170 insertions(+), 101 deletions(-) diff --git a/packages/vx-scale/src/scales/band.ts b/packages/vx-scale/src/scales/band.ts index faac401c7..b26beea7c 100644 --- a/packages/vx-scale/src/scales/band.ts +++ b/packages/vx-scale/src/scales/band.ts @@ -2,21 +2,17 @@ import { scaleBand } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { ScaleTypeToD3Scale } from '../types/Scale'; -import applyRound from '../mixins/applyRound'; +import { updatePointScale } from './point'; export function updateBandScale( scale: ScaleTypeToD3Scale['band'], config: PickScaleConfigWithoutType<'band', Value, DiscreteInput>, ) { - const { align, domain, padding, paddingInner, paddingOuter, range } = config; + const { paddingInner, paddingOuter } = config; - if (domain) scale.domain(domain); - if (range) scale.range(range); - if (typeof padding !== 'undefined') scale.padding(padding); + updatePointScale(scale, config); if (typeof paddingInner !== 'undefined') scale.paddingInner(paddingInner); if (typeof paddingOuter !== 'undefined') scale.paddingOuter(paddingOuter); - if (typeof align !== 'undefined') scale.align(align); - applyRound(scale, config); return scale; } diff --git a/packages/vx-scale/src/scales/quantile.ts b/packages/vx-scale/src/scales/quantile.ts index 49bdadb68..89da01557 100644 --- a/packages/vx-scale/src/scales/quantile.ts +++ b/packages/vx-scale/src/scales/quantile.ts @@ -2,7 +2,6 @@ import { scaleQuantile } from 'd3-scale'; import { Value } from '../types/Base'; import { ScaleTypeToD3Scale } from '../types/Scale'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import applyInterpolate from '../mixins/applyInterpolate'; export function updateQuantileScale( scale: ScaleTypeToD3Scale['quantile'], @@ -13,8 +12,6 @@ export function updateQuantileScale( if (domain) scale.domain(domain); if (range) scale.range(range); - applyInterpolate(scale, config); - return scale; } diff --git a/packages/vx-scale/src/scales/squareRoot.ts b/packages/vx-scale/src/scales/squareRoot.ts index 594a9edb8..3f661983b 100644 --- a/packages/vx-scale/src/scales/squareRoot.ts +++ b/packages/vx-scale/src/scales/squareRoot.ts @@ -2,26 +2,13 @@ import { scaleSqrt } from 'd3-scale'; import { Value } from '../types/Base'; import { ScaleTypeToD3Scale } from '../types/Scale'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import applyInterpolate from '../mixins/applyInterpolate'; -import applyRound from '../mixins/applyRound'; -import applyZero from '../mixins/applyZero'; +import { updatePowScale } from './power'; export function updateSqrtScale( scale: ScaleTypeToD3Scale['sqrt'], config: PickScaleConfigWithoutType<'sqrt', Output>, ) { - const { domain, range, clamp = false, nice = false } = config; - - if (domain) scale.domain(domain); - if (nice) scale.nice(); - if (range) scale.range(range); - - scale.clamp(clamp); - applyInterpolate(scale, config); - applyRound(scale, config); - applyZero(scale, config); - - return scale; + return updatePowScale(scale, { ...config, exponent: 0.5 }); } export default function createSqrtScale( diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index ef1d641db..bebbb8edc 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -17,7 +17,7 @@ export type ContinuousDomain = ContinuousInput[]; // and add `type` property as discriminant of union type. type CreateScaleConfig = 'type'> = Pick< BaseScaleConfig, - 'type' | 'domain' | 'range' | 'reverse' | Fields + 'type' | 'domain' | 'range' | Fields >; export type LinearScaleConfig = CreateScaleConfig< @@ -58,15 +58,14 @@ export type SymlogScaleConfig = CreateScaleConfig< export type QuantileScaleConfig = CreateScaleConfig< 'quantile', ContinuousDomain, - Output[], - 'interpolate' + Output[] >; export type QuantizeScaleConfig = CreateScaleConfig< 'quantize', [ContinuousInput, ContinuousInput], Output[], - 'interpolate' | 'nice' | 'zero' + 'nice' | 'zero' >; export type ThresholdScaleConfig< @@ -140,6 +139,10 @@ export interface ScaleTypeToScaleConfig< export type ScaleType = keyof ScaleTypeToScaleConfig; +export type TimeScaleType = 'time' | 'utc'; + +export type DiscreteScaleType = 'ordinal' | 'point' | 'band'; + export type PickScaleConfig< T extends ScaleType, Output = Value, diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index 0308602f1..12b62a17a 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -1,46 +1,86 @@ -import { - ScaleLinear, - ScaleLogarithmic, - ScalePower, - ScaleSymLog, - ScaleTime, - ScaleQuantile, - ScaleQuantize, - ScaleThreshold, - ScaleOrdinal, - ScalePoint, - ScaleBand, -} from 'd3-scale'; -import { ScaleConfig, ScaleConfigToD3Scale } from './types/ScaleConfig'; -import { DefaultThresholdInput, D3Scale } from './types/Scale'; +import { ScaleTime, ScaleLinear } from 'd3-scale'; +import { ScaleConfig, PickScaleConfigWithoutType } from './types/ScaleConfig'; +import { DefaultThresholdInput, D3Scale, PickD3Scale } from './types/Scale'; import { StringLike, Value } from './types/Base'; -import { updateLinearScale } from './scales/linear'; +import { updateThresholdScale } from './scales/threshold'; +import { updateQuantileScale } from './scales/quantile'; +import { updateBandScale } from './scales/band'; +import { updatePointScale } from './scales/point'; +import { updateOrdinalScale } from './scales/ordinal'; import { updateLogScale } from './scales/log'; -import { updatePowScale } from './scales/power'; -import { updateSqrtScale } from './scales/squareRoot'; import { updateSymlogScale } from './scales/symlog'; -import { updateTimeScale } from './scales/time'; -import { updateUtcScale } from './scales/utc'; -import { updateQuantileScale } from './scales/quantile'; +import { updatePowScale } from './scales/power'; import { updateQuantizeScale } from './scales/quantize'; -import { updateThresholdScale } from './scales/threshold'; -import { updateOrdinalScale } from './scales/ordinal'; -import { updatePointScale } from './scales/point'; -import { updateBandScale } from './scales/band'; +import { updateTimeScale } from './scales/time'; +import { updateLinearScale } from './scales/linear'; -// Overload function for more strict typing, e.g., -// If the config is a linear config then a ScaleLinear will be returned -// instead of a union type of all scales. +// Overload function signature for more strict typing, e.g., +// If the scale is a ScaleLinear, the config is a linear config. -function updateScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput, - Config extends ScaleConfig ->( - scale: ScaleConfigToD3Scale, - config: Config, -): ScaleConfigToD3Scale; +function updateScale( + scale: PickD3Scale<'linear', Output>, + config: PickScaleConfigWithoutType<'linear', Output>, +): PickD3Scale<'linear', Output>; + +function updateScale( + scale: PickD3Scale<'log', Output>, + config: PickScaleConfigWithoutType<'log', Output>, +): PickD3Scale<'log', Output>; + +function updateScale( + scale: PickD3Scale<'pow', Output>, + config: PickScaleConfigWithoutType<'pow', Output>, +): PickD3Scale<'pow', Output>; + +function updateScale( + scale: PickD3Scale<'sqrt', Output>, + config: PickScaleConfigWithoutType<'sqrt', Output>, +): PickD3Scale<'sqrt', Output>; + +function updateScale( + scale: PickD3Scale<'symlog', Output>, + config: PickScaleConfigWithoutType<'symlog', Output>, +): PickD3Scale<'symlog', Output>; + +function updateScale( + scale: PickD3Scale<'time', Output>, + config: PickScaleConfigWithoutType<'time', Output>, +): PickD3Scale<'time', Output>; + +function updateScale( + scale: PickD3Scale<'utc', Output>, + config: PickScaleConfigWithoutType<'utc', Output>, +): PickD3Scale<'utc', Output>; + +function updateScale( + scale: PickD3Scale<'quantile', Output>, + config: PickScaleConfigWithoutType<'quantile', Output>, +): PickD3Scale<'quantile', Output>; + +function updateScale( + scale: PickD3Scale<'quantize', Output>, + config: PickScaleConfigWithoutType<'quantize', Output>, +): PickD3Scale<'quantize', Output>; + +function updateScale( + scale: PickD3Scale<'threshold', Output, StringLike, ThresholdInput>, + config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>, +): PickD3Scale<'threshold', Output, StringLike, ThresholdInput>; + +function updateScale( + scale: PickD3Scale<'ordinal', Output, DiscreteInput>, + config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>, +): PickD3Scale<'ordinal', Output, DiscreteInput>; + +function updateScale( + scale: PickD3Scale<'point', Output, DiscreteInput>, + config: PickScaleConfigWithoutType<'point', Output, DiscreteInput>, +): PickD3Scale<'point', Output, DiscreteInput>; + +function updateScale( + scale: PickD3Scale<'band', Output, DiscreteInput>, + config: PickScaleConfigWithoutType<'band', Output, DiscreteInput>, +): PickD3Scale<'band', Output, DiscreteInput>; function updateScale< Output extends Value, @@ -54,47 +94,93 @@ function updateScale< function updateScale< Output extends Value, DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput, - Config extends ScaleConfig + ThresholdInput extends DefaultThresholdInput >( - scale: ScaleConfigToD3Scale, - config?: ScaleConfig, + scale: D3Scale, + config?: Omit, 'type'>, ) { + const scaleOut = scale.copy() as D3Scale; + + // If a config is not specified, just return a copy if (typeof config === 'undefined') { - return scale.copy(); + return scaleOut; + } + + // Try a sequence of typeguards to figure out the scale type + // and cast the config to correct type. + // Function overloading above should ensure the scale and config + // are compatible matches. + + if ('paddingInner' in scaleOut) { + // Band scale + return updateBandScale( + scaleOut, + config as PickScaleConfigWithoutType<'band', Output, DiscreteInput, ThresholdInput>, + ); + } + + if ('padding' in scaleOut) { + // Point scale + return updatePointScale( + scaleOut, + config as PickScaleConfigWithoutType<'point', Output, DiscreteInput, ThresholdInput>, + ); } - switch (config.type) { - case 'linear': - return updateLinearScale(scale.copy() as ScaleLinear, config); - case 'log': - return updateLogScale(scale as ScaleLogarithmic, config); - case 'pow': - return updatePowScale(scale as ScalePower, config); - case 'sqrt': - return updateSqrtScale(scale as ScalePower, config); - case 'symlog': - return updateSymlogScale(scale as ScaleSymLog, config); - case 'time': - return updateTimeScale(scale as ScaleTime, config); - case 'utc': - return updateUtcScale(scale as ScaleTime, config); - case 'quantile': - return updateQuantileScale(scale as ScaleQuantile, config); - case 'quantize': - return updateQuantizeScale(scale as ScaleQuantize, config); - case 'threshold': - return updateThresholdScale(scale as ScaleThreshold, config); - case 'ordinal': - return updateOrdinalScale(scale as ScaleOrdinal, config); - case 'point': - return updatePointScale(scale as ScalePoint, config); - case 'band': - return updateBandScale(scale as ScaleBand, config); - default: - // @ts-ignore - throw new Error(`Invalid scale type: ${config.type}`); + if ('unknown' in scaleOut) { + // Ordinal scale + return updateOrdinalScale( + scaleOut, + config as PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput, ThresholdInput>, + ); } + + if ('quantiles' in scaleOut) { + // Quantile scale + return updateQuantileScale(scaleOut, config as PickScaleConfigWithoutType<'quantile', Output>); + } + + if ('base' in scaleOut) { + // Log scale + return updateLogScale(scaleOut, config as PickScaleConfigWithoutType<'log', Output>); + } + + if ('exponent' in scaleOut) { + // Pow or Sqrt scale + return updatePowScale(scaleOut, config as PickScaleConfigWithoutType<'pow', Output>); + } + + if ('constant' in scaleOut) { + // Symlog scale + return updateSymlogScale(scaleOut, config as PickScaleConfigWithoutType<'symlog', Output>); + } + + if ('clamp' in scaleOut) { + // Linear, Time or Utc scales + if (scaleOut.ticks()[0] instanceof Date) { + // Time scale + return updateTimeScale( + scaleOut as ScaleTime, + config as PickScaleConfigWithoutType<'time', Output>, + ); + } + // Linear scale + return updateLinearScale( + scaleOut as ScaleLinear, + config as PickScaleConfigWithoutType<'linear', Output>, + ); + } + + if ('nice' in scaleOut) { + // Symlog scale + return updateQuantizeScale(scaleOut, config as PickScaleConfigWithoutType<'quantize', Output>); + } + + // The last type remaining is Threshold scale + return updateThresholdScale( + scaleOut, + config as PickScaleConfigWithoutType<'threshold', Output, DiscreteInput, ThresholdInput>, + ); } export default updateScale; From bbebebfb616452cab13093f15a05ab7666cae6ac Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 17:10:46 -0700 Subject: [PATCH 13/42] feat: use operator --- packages/vx-scale/package.json | 6 +- packages/vx-scale/src/createScale.ts | 10 +- .../vx-scale/src/mixins/applyInterpolate.ts | 15 --- packages/vx-scale/src/mixins/applyRound.ts | 18 ---- packages/vx-scale/src/mixins/applyZero.ts | 16 ---- packages/vx-scale/src/operators/clamp.ts | 16 ++++ packages/vx-scale/src/operators/domain.ts | 17 ++++ .../vx-scale/src/operators/interpolate.ts | 19 ++++ packages/vx-scale/src/operators/nice.ts | 94 +++++++++++++++++++ packages/vx-scale/src/operators/range.ts | 17 ++++ packages/vx-scale/src/operators/round.ts | 24 +++++ .../vx-scale/src/operators/scaleOperator.ts | 49 ++++++++++ packages/vx-scale/src/operators/zero.ts | 21 +++++ packages/vx-scale/src/scales/linear.ts | 31 ++---- packages/vx-scale/src/scales/log.ts | 4 +- packages/vx-scale/src/types/Scale.ts | 2 +- packages/vx-scale/src/types/ScaleConfig.ts | 12 ++- packages/vx-scale/src/util/updateScale.ts | 9 -- .../createColorInterpolator.ts | 0 yarn.lock | 4 +- 20 files changed, 292 insertions(+), 92 deletions(-) delete mode 100644 packages/vx-scale/src/mixins/applyInterpolate.ts delete mode 100644 packages/vx-scale/src/mixins/applyRound.ts delete mode 100644 packages/vx-scale/src/mixins/applyZero.ts create mode 100644 packages/vx-scale/src/operators/clamp.ts create mode 100644 packages/vx-scale/src/operators/domain.ts create mode 100644 packages/vx-scale/src/operators/interpolate.ts create mode 100644 packages/vx-scale/src/operators/nice.ts create mode 100644 packages/vx-scale/src/operators/range.ts create mode 100644 packages/vx-scale/src/operators/round.ts create mode 100644 packages/vx-scale/src/operators/scaleOperator.ts create mode 100644 packages/vx-scale/src/operators/zero.ts delete mode 100644 packages/vx-scale/src/util/updateScale.ts rename packages/vx-scale/src/{util => utils}/createColorInterpolator.ts (100%) diff --git a/packages/vx-scale/package.json b/packages/vx-scale/package.json index 130ca85a2..139abfb3c 100644 --- a/packages/vx-scale/package.json +++ b/packages/vx-scale/package.json @@ -28,10 +28,12 @@ }, "homepage": "https://github.com/hshoff/vx#readme", "dependencies": { - "@types/d3-scale": "^2.1.1", "@types/d3-interpolate": "^1.3.1", + "@types/d3-scale": "^2.1.1", + "@types/d3-time": "^1.0.10", + "d3-interpolate": "^1.4.0", "d3-scale": "^3.0.1", - "d3-interpolate": "^1.4.0" + "d3-time": "^1.1.0" }, "publishConfig": { "access": "public" diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 668615a14..3b43182ea 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -1,5 +1,5 @@ -import { ScaleConfig, ScaleTypeToScaleConfig, ScaleConfigToD3Scale } from './types/ScaleConfig'; -import { ScaleTypeToD3Scale, DefaultThresholdInput } from './types/Scale'; +import { ScaleConfig, ScaleConfigToD3Scale, PickScaleConfigWithoutType } from './types/ScaleConfig'; +import { DefaultThresholdInput, PickD3Scale } from './types/Scale'; import { StringLike, Value } from './types/Base'; import createLinearScale from './scales/linear'; import createLogScale from './scales/log'; @@ -31,8 +31,8 @@ function createScale< DiscreteInput extends StringLike, ThresholdInput extends DefaultThresholdInput >( - config: Omit['linear'], 'type'>, -): ScaleTypeToD3Scale['linear']; + config: PickScaleConfigWithoutType<'linear', Output>, +): PickD3Scale<'linear', Output, DiscreteInput, ThresholdInput>; // Actual implementation @@ -43,7 +43,7 @@ function createScale< >( config: | ScaleConfig - | Omit['linear'], 'type'>, + | PickScaleConfigWithoutType<'linear', Output>, ) { if ('type' in config) { switch (config.type) { diff --git a/packages/vx-scale/src/mixins/applyInterpolate.ts b/packages/vx-scale/src/mixins/applyInterpolate.ts deleted file mode 100644 index 0f888271b..000000000 --- a/packages/vx-scale/src/mixins/applyInterpolate.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { InterpolatorFactory } from 'd3-scale'; -import { Value } from '../types/Base'; -import { D3Scale } from '../types/Scale'; -import { ScaleInterpolate, ScaleInterpolateParams } from '../types/ScaleInterpolate'; -import createColorInterpolator from '../util/createColorInterpolator'; - -export default function applyInterpolate( - scale: D3Scale, - config: { interpolate?: ScaleInterpolate | ScaleInterpolateParams }, -) { - if (config.interpolate && 'interpolate' in scale) { - const interpolator = createColorInterpolator(config.interpolate); - scale.interpolate((interpolator as unknown) as InterpolatorFactory); - } -} diff --git a/packages/vx-scale/src/mixins/applyRound.ts b/packages/vx-scale/src/mixins/applyRound.ts deleted file mode 100644 index 6a3b58745..000000000 --- a/packages/vx-scale/src/mixins/applyRound.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { interpolateRound } from 'd3-interpolate'; -import { InterpolatorFactory } from 'd3-scale'; -import { Value, StringLike } from '../types/Base'; -import { D3Scale, DefaultThresholdInput } from '../types/Scale'; - -export default function applyRound< - Output = Value, - DiscreteInput extends StringLike = StringLike, - ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput ->(scale: D3Scale, config: { round?: boolean }) { - if (typeof config.round !== 'undefined') { - if ('round' in scale) { - scale.round(config.round); - } else if ('interpolate' in scale && config.round) { - scale.interpolate((interpolateRound as unknown) as InterpolatorFactory); - } - } -} diff --git a/packages/vx-scale/src/mixins/applyZero.ts b/packages/vx-scale/src/mixins/applyZero.ts deleted file mode 100644 index d7a28cb0f..000000000 --- a/packages/vx-scale/src/mixins/applyZero.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Value } from '../types/Base'; -import { PickD3Scale } from '../types/Scale'; - -export default function applyZero( - scale: PickD3Scale<'linear' | 'pow' | 'sqrt' | 'symlog' | 'quantize', Output>, - config: { zero?: boolean }, -) { - if (config.zero === true) { - const domain = scale.domain() as number[]; - const [a, b] = domain; - const isDescending = b < a; - const [min, max] = isDescending ? [b, a] : [a, b]; - const domainWithZero = [Math.min(0, min), Math.max(0, max)]; - scale.domain(isDescending ? domainWithZero.reverse() : domainWithZero); - } -} diff --git a/packages/vx-scale/src/operators/clamp.ts b/packages/vx-scale/src/operators/clamp.ts new file mode 100644 index 000000000..3b738c8fb --- /dev/null +++ b/packages/vx-scale/src/operators/clamp.ts @@ -0,0 +1,16 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyClamp< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('clamp' in scale && 'clamp' in config && typeof config.clamp !== 'undefined') { + scale.clamp(config.clamp); + } +} diff --git a/packages/vx-scale/src/operators/domain.ts b/packages/vx-scale/src/operators/domain.ts new file mode 100644 index 000000000..7d79cf51d --- /dev/null +++ b/packages/vx-scale/src/operators/domain.ts @@ -0,0 +1,17 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyDomain< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if (config.domain) { + // @ts-ignore + scale.domain(config.domain); + } +} diff --git a/packages/vx-scale/src/operators/interpolate.ts b/packages/vx-scale/src/operators/interpolate.ts new file mode 100644 index 000000000..18f8188fa --- /dev/null +++ b/packages/vx-scale/src/operators/interpolate.ts @@ -0,0 +1,19 @@ +import { InterpolatorFactory } from 'd3-scale'; +import { StringLike } from '../types/Base'; +import { D3Scale, DefaultThresholdInput } from '../types/Scale'; +import createColorInterpolator from '../utils/createColorInterpolator'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyInterpolate< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('interpolate' in config && config.interpolate && 'interpolate' in scale) { + const interpolator = createColorInterpolator(config.interpolate); + scale.interpolate((interpolator as unknown) as InterpolatorFactory); + } +} diff --git a/packages/vx-scale/src/operators/nice.ts b/packages/vx-scale/src/operators/nice.ts new file mode 100644 index 000000000..39de7a9fa --- /dev/null +++ b/packages/vx-scale/src/operators/nice.ts @@ -0,0 +1,94 @@ +import { + timeSecond, + timeMinute, + timeHour, + timeDay, + timeYear, + timeMonth, + timeWeek, + utcSecond, + utcMinute, + utcHour, + utcDay, + utcWeek, + utcMonth, + utcYear, + CountableTimeInterval, +} from 'd3-time'; +import { ScaleTime } from 'd3-scale'; +import { StringLike } from '../types/Base'; +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; +import { NiceTime } from '../types/Nice'; + +const localTimeIntervals: { + [key in NiceTime]: CountableTimeInterval; +} = { + day: timeDay, + hour: timeHour, + minute: timeMinute, + month: timeMonth, + second: timeSecond, + week: timeWeek, + year: timeYear, +}; + +const utcIntervals: { + [key in NiceTime]: CountableTimeInterval; +} = { + day: utcDay, + hour: utcHour, + minute: utcMinute, + month: utcMonth, + second: utcSecond, + week: utcWeek, + year: utcYear, +}; + +const TEST_TIME = new Date(Date.UTC(2020, 1, 2, 3, 4, 5)); +const TEST_FORMAT = '%Y-%m-%d %H:%M'; + +/** + * Check if the scale is utc or time scale + * @param scale time or utc scale + */ +function isUtcScale(scale: ScaleTime) { + // The only difference between time and utc scale is + // whether the tick format is utcFormat or timeFormat + return scale.tickFormat(1, TEST_FORMAT)(TEST_TIME) === '2020-02-01 19:04'; +} + +export default function applyNice< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('nice' in config && typeof config.nice !== 'undefined' && 'nice' in scale) { + const { nice } = config; + if (typeof nice === 'boolean') { + if (nice) { + scale.nice(); + } + } else if (typeof nice === 'number') { + scale.nice(nice); + } else { + const timeScale = scale as ScaleTime; + const isUtc = isUtcScale(timeScale); + if (typeof nice === 'string') { + timeScale.nice(isUtc ? utcIntervals[nice] : localTimeIntervals[nice]); + } else { + const { interval, step } = nice; + const parsedInterval = (isUtc + ? utcIntervals[interval] + : localTimeIntervals[interval] + ).every(step); + if (parsedInterval !== null) { + timeScale.nice(parsedInterval as CountableTimeInterval); + } + } + } + } +} diff --git a/packages/vx-scale/src/operators/range.ts b/packages/vx-scale/src/operators/range.ts new file mode 100644 index 000000000..a48d7f906 --- /dev/null +++ b/packages/vx-scale/src/operators/range.ts @@ -0,0 +1,17 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyRange< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if (config.range) { + // @ts-ignore + scale.range(config.range); + } +} diff --git a/packages/vx-scale/src/operators/round.ts b/packages/vx-scale/src/operators/round.ts new file mode 100644 index 000000000..1c64ff258 --- /dev/null +++ b/packages/vx-scale/src/operators/round.ts @@ -0,0 +1,24 @@ +import { interpolateRound } from 'd3-interpolate'; +import { InterpolatorFactory } from 'd3-scale'; +import { StringLike } from '../types/Base'; +import { D3Scale, DefaultThresholdInput } from '../types/Scale'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyRound< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('round' in config && typeof config.round !== 'undefined') { + if (config.round && 'interpolate' in config && typeof config.interpolate !== 'undefined') { + console.warn(`can't round and interpolate`); + } else if ('round' in scale) { + scale.round(config.round); + } else if ('interpolate' in scale && config.round) { + scale.interpolate((interpolateRound as unknown) as InterpolatorFactory); + } + } +} diff --git a/packages/vx-scale/src/operators/scaleOperator.ts b/packages/vx-scale/src/operators/scaleOperator.ts new file mode 100644 index 000000000..68d70cfe2 --- /dev/null +++ b/packages/vx-scale/src/operators/scaleOperator.ts @@ -0,0 +1,49 @@ +import { DefaultThresholdInput, PickD3Scale } from '../types/Scale'; +import { ScaleType, PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import { Value, StringLike } from '../types/Base'; +import domain from './domain'; +import range from './range'; +import clamp from './clamp'; +import interpolate from './interpolate'; +import nice from './nice'; +import round from './round'; +import zero from './zero'; + +const operators = { + // domain => nice => zero + domain, + nice, + zero, + + // interpolate before round + interpolate, + round, + + // Order does not matter for these operators + range, + clamp, +}; + +type OperatorType = keyof typeof operators; + +const orderedOps = Object.keys(operators) as OperatorType[]; + +export default function scaleOperator(...ops: OperatorType[]) { + const selection = new Set(ops); + const selectedOps = orderedOps.filter(o => selection.has(o)); + + return function applyOperators< + Output = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput + >( + scale: PickD3Scale, + config: PickScaleConfigWithoutType, + ) { + selectedOps.forEach(op => { + operators[op](scale, config); + }); + + return scale; + }; +} diff --git a/packages/vx-scale/src/operators/zero.ts b/packages/vx-scale/src/operators/zero.ts new file mode 100644 index 000000000..f832289af --- /dev/null +++ b/packages/vx-scale/src/operators/zero.ts @@ -0,0 +1,21 @@ +import { StringLike } from '../types/Base'; +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyZero< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('zero' in config && config.zero === true) { + const domain = scale.domain() as number[]; + const [a, b] = domain; + const isDescending = b < a; + const [min, max] = isDescending ? [b, a] : [a, b]; + const domainWithZero = [Math.min(0, min), Math.max(0, max)]; + scale.domain(isDescending ? domainWithZero.reverse() : domainWithZero); + } +} diff --git a/packages/vx-scale/src/scales/linear.ts b/packages/vx-scale/src/scales/linear.ts index a5c1dad26..4e0858cb7 100644 --- a/packages/vx-scale/src/scales/linear.ts +++ b/packages/vx-scale/src/scales/linear.ts @@ -1,28 +1,17 @@ import { scaleLinear } from 'd3-scale'; import { Value } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import applyInterpolate from '../mixins/applyInterpolate'; -import applyRound from '../mixins/applyRound'; -import applyZero from '../mixins/applyZero'; -import { ScaleTypeToD3Scale } from '../types/Scale'; +import scaleOperator from '../operators/scaleOperator'; -export function updateLinearScale( - scale: ScaleTypeToD3Scale['linear'], - config: PickScaleConfigWithoutType<'linear', Output>, -) { - const { domain, range, clamp = false, nice = false } = config; - - if (domain) scale.domain(domain); - if (nice) scale.nice(); - if (range) scale.range(range); - - scale.clamp(clamp); - applyInterpolate(scale, config); - applyRound(scale, config); - applyZero(scale, config); - - return scale; -} +export const updateLinearScale = scaleOperator<'linear'>( + 'domain', + 'range', + 'clamp', + 'interpolate', + 'nice', + 'round', + 'zero', +); export default function createLinearScale( config: PickScaleConfigWithoutType<'linear', Output>, diff --git a/packages/vx-scale/src/scales/log.ts b/packages/vx-scale/src/scales/log.ts index 2a8b306b2..61ecb5639 100644 --- a/packages/vx-scale/src/scales/log.ts +++ b/packages/vx-scale/src/scales/log.ts @@ -2,8 +2,8 @@ import { scaleLog } from 'd3-scale'; import { Value } from '../types/Base'; import { ScaleTypeToD3Scale } from '../types/Scale'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import applyInterpolate from '../mixins/applyInterpolate'; -import applyRound from '../mixins/applyRound'; +import applyInterpolate from '../operators/interpolate'; +import applyRound from '../operators/round'; export function updateLogScale( scale: ScaleTypeToD3Scale['log'], diff --git a/packages/vx-scale/src/types/Scale.ts b/packages/vx-scale/src/types/Scale.ts index fc8c3baaf..f707ad2b7 100644 --- a/packages/vx-scale/src/types/Scale.ts +++ b/packages/vx-scale/src/types/Scale.ts @@ -51,7 +51,7 @@ export interface ScaleTypeToD3Scale< } export type PickD3Scale< - T extends keyof ScaleTypeToD3Scale, + T extends keyof ScaleTypeToD3Scale, Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index bebbb8edc..233f42a19 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -150,12 +150,16 @@ export type PickScaleConfig< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf, T>>; +type OmitType = { + [key in keyof T]: Omit; +}; + export type PickScaleConfigWithoutType< T extends ScaleType, Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput -> = Omit, 'type'>; +> = ValueOf>, T>>; export type ScaleConfig< Output = Value, @@ -163,6 +167,12 @@ export type ScaleConfig< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>; +export type ScaleConfigWithoutType< + Output = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +> = PickScaleConfigWithoutType; + export type ScaleConfigToD3Scale< Config extends ScaleConfig, Output = Value, diff --git a/packages/vx-scale/src/util/updateScale.ts b/packages/vx-scale/src/util/updateScale.ts deleted file mode 100644 index 62c08e152..000000000 --- a/packages/vx-scale/src/util/updateScale.ts +++ /dev/null @@ -1,9 +0,0 @@ -const has = Object.prototype.hasOwnProperty; - -export default function updateScale(scale: any, { ...args }: any = {}) { - const nextScale = scale.copy(); - Object.keys(args).forEach(key => { - if (has.call(nextScale, key)) nextScale[key](args[key]); - }); - return nextScale; -} diff --git a/packages/vx-scale/src/util/createColorInterpolator.ts b/packages/vx-scale/src/utils/createColorInterpolator.ts similarity index 100% rename from packages/vx-scale/src/util/createColorInterpolator.ts rename to packages/vx-scale/src/utils/createColorInterpolator.ts diff --git a/yarn.lock b/yarn.lock index 032bb1763..68677b894 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2741,7 +2741,7 @@ resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.1.0.tgz#011e0fb7937be34a9a8f580ae1e2f2f1336a8a22" integrity sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA== -"@types/d3-time@*": +"@types/d3-time@*", "@types/d3-time@^1.0.10": version "1.0.10" resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.10.tgz#d338c7feac93a98a32aac875d1100f92c7b61f4f" integrity sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw== @@ -5334,7 +5334,7 @@ d3-time-format@2, d3-time-format@^2.0.5: dependencies: d3-time "1" -d3-time@1: +d3-time@1, d3-time@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1" integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA== From b57bc94fa9b6a49810da393980e81189e698b820 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 17:30:49 -0700 Subject: [PATCH 14/42] refactor: use operator for all --- packages/vx-scale/src/operators/align.ts | 16 +++++++++ packages/vx-scale/src/operators/base.ts | 16 +++++++++ packages/vx-scale/src/operators/constant.ts | 16 +++++++++ packages/vx-scale/src/operators/exponent.ts | 16 +++++++++ packages/vx-scale/src/operators/padding.ts | 30 +++++++++++++++++ .../vx-scale/src/operators/scaleOperator.ts | 14 +++++++- packages/vx-scale/src/operators/unknown.ts | 16 +++++++++ packages/vx-scale/src/scales/band.ts | 22 +++++-------- packages/vx-scale/src/scales/log.ts | 30 ++++++----------- packages/vx-scale/src/scales/ordinal.ts | 15 ++------- packages/vx-scale/src/scales/point.ts | 24 +++++--------- packages/vx-scale/src/scales/power.ts | 33 +++++++------------ packages/vx-scale/src/scales/quantile.ts | 14 ++------ packages/vx-scale/src/scales/quantize.ts | 18 ++-------- packages/vx-scale/src/scales/squareRoot.ts | 18 +++++----- packages/vx-scale/src/scales/symlog.ts | 29 ++++++---------- packages/vx-scale/src/scales/threshold.ts | 18 ++-------- packages/vx-scale/src/scales/time.ts | 28 +++++----------- packages/vx-scale/src/scales/utc.ts | 11 +++++-- 19 files changed, 207 insertions(+), 177 deletions(-) create mode 100644 packages/vx-scale/src/operators/align.ts create mode 100644 packages/vx-scale/src/operators/base.ts create mode 100644 packages/vx-scale/src/operators/constant.ts create mode 100644 packages/vx-scale/src/operators/exponent.ts create mode 100644 packages/vx-scale/src/operators/padding.ts create mode 100644 packages/vx-scale/src/operators/unknown.ts diff --git a/packages/vx-scale/src/operators/align.ts b/packages/vx-scale/src/operators/align.ts new file mode 100644 index 000000000..2d810c9de --- /dev/null +++ b/packages/vx-scale/src/operators/align.ts @@ -0,0 +1,16 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyAlign< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('align' in scale && 'align' in config && typeof config.align !== 'undefined') { + scale.align(config.align); + } +} diff --git a/packages/vx-scale/src/operators/base.ts b/packages/vx-scale/src/operators/base.ts new file mode 100644 index 000000000..c0507356c --- /dev/null +++ b/packages/vx-scale/src/operators/base.ts @@ -0,0 +1,16 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyBase< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('base' in scale && 'base' in config && typeof config.base !== 'undefined') { + scale.base(config.base); + } +} diff --git a/packages/vx-scale/src/operators/constant.ts b/packages/vx-scale/src/operators/constant.ts new file mode 100644 index 000000000..7a730cac3 --- /dev/null +++ b/packages/vx-scale/src/operators/constant.ts @@ -0,0 +1,16 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyConstant< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('constant' in scale && 'constant' in config && typeof config.constant !== 'undefined') { + scale.constant(config.constant); + } +} diff --git a/packages/vx-scale/src/operators/exponent.ts b/packages/vx-scale/src/operators/exponent.ts new file mode 100644 index 000000000..f786cfd0e --- /dev/null +++ b/packages/vx-scale/src/operators/exponent.ts @@ -0,0 +1,16 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyExponent< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('exponent' in scale && 'exponent' in config && typeof config.exponent !== 'undefined') { + scale.exponent(config.exponent); + } +} diff --git a/packages/vx-scale/src/operators/padding.ts b/packages/vx-scale/src/operators/padding.ts new file mode 100644 index 000000000..2aaeec559 --- /dev/null +++ b/packages/vx-scale/src/operators/padding.ts @@ -0,0 +1,30 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyPadding< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('padding' in scale && 'padding' in config && typeof config.padding !== 'undefined') { + scale.padding(config.padding); + } + if ( + 'paddingInner' in scale && + 'paddingInner' in config && + typeof config.paddingInner !== 'undefined' + ) { + scale.paddingInner(config.paddingInner); + } + if ( + 'paddingOuter' in scale && + 'paddingOuter' in config && + typeof config.paddingOuter !== 'undefined' + ) { + scale.paddingOuter(config.paddingOuter); + } +} diff --git a/packages/vx-scale/src/operators/scaleOperator.ts b/packages/vx-scale/src/operators/scaleOperator.ts index 68d70cfe2..77cddf4c3 100644 --- a/packages/vx-scale/src/operators/scaleOperator.ts +++ b/packages/vx-scale/src/operators/scaleOperator.ts @@ -3,10 +3,16 @@ import { ScaleType, PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { Value, StringLike } from '../types/Base'; import domain from './domain'; import range from './range'; +import align from './align'; +import base from './base'; import clamp from './clamp'; +import constant from './constant'; +import exponent from './exponent'; import interpolate from './interpolate'; import nice from './nice'; +import padding from './padding'; import round from './round'; +import unknown from './unknown'; import zero from './zero'; const operators = { @@ -20,8 +26,14 @@ const operators = { round, // Order does not matter for these operators - range, + align, + base, clamp, + constant, + exponent, + padding, + range, + unknown, }; type OperatorType = keyof typeof operators; diff --git a/packages/vx-scale/src/operators/unknown.ts b/packages/vx-scale/src/operators/unknown.ts new file mode 100644 index 000000000..df6a467c6 --- /dev/null +++ b/packages/vx-scale/src/operators/unknown.ts @@ -0,0 +1,16 @@ +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { StringLike } from '../types/Base'; +import { ScaleConfigWithoutType } from '../types/ScaleConfig'; + +export default function applyUnknown< + Output, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>( + scale: D3Scale, + config: ScaleConfigWithoutType, +) { + if ('unknown' in scale && 'unknown' in config && typeof config.unknown !== 'undefined') { + scale.unknown(config.unknown); + } +} diff --git a/packages/vx-scale/src/scales/band.ts b/packages/vx-scale/src/scales/band.ts index b26beea7c..5e6bbacb0 100644 --- a/packages/vx-scale/src/scales/band.ts +++ b/packages/vx-scale/src/scales/band.ts @@ -1,21 +1,15 @@ import { scaleBand } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale } from '../types/Scale'; -import { updatePointScale } from './point'; +import scaleOperator from '../operators/scaleOperator'; -export function updateBandScale( - scale: ScaleTypeToD3Scale['band'], - config: PickScaleConfigWithoutType<'band', Value, DiscreteInput>, -) { - const { paddingInner, paddingOuter } = config; - - updatePointScale(scale, config); - if (typeof paddingInner !== 'undefined') scale.paddingInner(paddingInner); - if (typeof paddingOuter !== 'undefined') scale.paddingOuter(paddingOuter); - - return scale; -} +export const updateBandScale = scaleOperator<'band'>( + 'domain', + 'range', + 'align', + 'padding', + 'round', +); export default function createBandScale( config: PickScaleConfigWithoutType<'band', Value, DiscreteInput>, diff --git a/packages/vx-scale/src/scales/log.ts b/packages/vx-scale/src/scales/log.ts index 61ecb5639..3f8735f13 100644 --- a/packages/vx-scale/src/scales/log.ts +++ b/packages/vx-scale/src/scales/log.ts @@ -1,27 +1,17 @@ import { scaleLog } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToD3Scale } from '../types/Scale'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import applyInterpolate from '../operators/interpolate'; -import applyRound from '../operators/round'; +import scaleOperator from '../operators/scaleOperator'; -export function updateLogScale( - scale: ScaleTypeToD3Scale['log'], - config: PickScaleConfigWithoutType<'log', Output>, -) { - const { domain, range, base, clamp = false, nice = false } = config; - - if (base) scale.base(base); - if (domain) scale.domain(domain); - if (nice) scale.nice(); - if (range) scale.range(range); - - scale.clamp(clamp); - applyInterpolate(scale, config); - applyRound(scale, config); - - return scale; -} +export const updateLogScale = scaleOperator<'log'>( + 'domain', + 'range', + 'base', + 'clamp', + 'interpolate', + 'nice', + 'round', +); export default function createLogScale( config: PickScaleConfigWithoutType<'log', Output>, diff --git a/packages/vx-scale/src/scales/ordinal.ts b/packages/vx-scale/src/scales/ordinal.ts index b6f3fa9cb..cfb2ae22a 100644 --- a/packages/vx-scale/src/scales/ordinal.ts +++ b/packages/vx-scale/src/scales/ordinal.ts @@ -1,20 +1,9 @@ import { scaleOrdinal } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale } from '../types/Scale'; +import scaleOperator from '../operators/scaleOperator'; -export function updateOrdinalScale( - scale: ScaleTypeToD3Scale['ordinal'], - config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>, -) { - const { domain, range, unknown } = config; - - if (domain) scale.domain(domain); - if (range) scale.range(range); - if (unknown) scale.unknown(unknown); - - return scale; -} +export const updateOrdinalScale = scaleOperator<'ordinal'>('domain', 'range', 'unknown'); export default function createOrdinalScale< DiscreteInput extends StringLike = StringLike, diff --git a/packages/vx-scale/src/scales/point.ts b/packages/vx-scale/src/scales/point.ts index a43bed0b7..8872345b1 100644 --- a/packages/vx-scale/src/scales/point.ts +++ b/packages/vx-scale/src/scales/point.ts @@ -1,23 +1,15 @@ import { scalePoint } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale } from '../types/Scale'; -import applyRound from '../mixins/applyRound'; +import scaleOperator from '../operators/scaleOperator'; -export function updatePointScale( - scale: ScaleTypeToD3Scale['point'], - config: PickScaleConfigWithoutType<'point', Value, DiscreteInput>, -) { - const { align, domain, padding, range } = config; - - if (domain) scale.domain(domain); - if (range) scale.range(range); - if (typeof padding !== 'undefined') scale.padding(padding); - if (typeof align !== 'undefined') scale.align(align); - applyRound(scale, config); - - return scale; -} +export const updatePointScale = scaleOperator<'point'>( + 'domain', + 'range', + 'align', + 'padding', + 'round', +); export default function createPointScale( config: PickScaleConfigWithoutType<'point', Value, DiscreteInput>, diff --git a/packages/vx-scale/src/scales/power.ts b/packages/vx-scale/src/scales/power.ts index 2808c93ca..9c56bab14 100644 --- a/packages/vx-scale/src/scales/power.ts +++ b/packages/vx-scale/src/scales/power.ts @@ -1,29 +1,18 @@ import { scalePow } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToD3Scale } from '../types/Scale'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import applyInterpolate from '../mixins/applyInterpolate'; -import applyRound from '../mixins/applyRound'; -import applyZero from '../mixins/applyZero'; +import scaleOperator from '../operators/scaleOperator'; -export function updatePowScale( - scale: ScaleTypeToD3Scale['pow'], - config: PickScaleConfigWithoutType<'pow', Output>, -) { - const { domain, range, clamp = false, exponent, nice = false } = config; - - if (domain) scale.domain(domain); - if (nice) scale.nice(); - if (range) scale.range(range); - - scale.clamp(clamp); - if (exponent) scale.exponent(exponent); - applyInterpolate(scale, config); - applyRound(scale, config); - applyZero(scale, config); - - return scale; -} +export const updatePowScale = scaleOperator<'pow'>( + 'domain', + 'range', + 'clamp', + 'exponent', + 'interpolate', + 'nice', + 'round', + 'zero', +); export default function createPowScale( config: PickScaleConfigWithoutType<'pow', Output>, diff --git a/packages/vx-scale/src/scales/quantile.ts b/packages/vx-scale/src/scales/quantile.ts index 89da01557..b9405a844 100644 --- a/packages/vx-scale/src/scales/quantile.ts +++ b/packages/vx-scale/src/scales/quantile.ts @@ -1,19 +1,9 @@ import { scaleQuantile } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToD3Scale } from '../types/Scale'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; +import scaleOperator from '../operators/scaleOperator'; -export function updateQuantileScale( - scale: ScaleTypeToD3Scale['quantile'], - config: PickScaleConfigWithoutType<'quantile', Output>, -) { - const { domain, range } = config; - - if (domain) scale.domain(domain); - if (range) scale.range(range); - - return scale; -} +export const updateQuantileScale = scaleOperator<'quantile'>('domain', 'range'); export default function createQuantileScale( config: PickScaleConfigWithoutType<'quantile', Output>, diff --git a/packages/vx-scale/src/scales/quantize.ts b/packages/vx-scale/src/scales/quantize.ts index 153d1922d..7cdff6a43 100644 --- a/packages/vx-scale/src/scales/quantize.ts +++ b/packages/vx-scale/src/scales/quantize.ts @@ -1,23 +1,9 @@ import { scaleQuantize } from 'd3-scale'; import { Value } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale } from '../types/Scale'; -import applyZero from '../mixins/applyZero'; +import scaleOperator from '../operators/scaleOperator'; -export function updateQuantizeScale( - scale: ScaleTypeToD3Scale['quantize'], - config: PickScaleConfigWithoutType<'quantize', Output>, -) { - const { domain, range, nice = false } = config; - - if (domain) scale.domain(domain); - if (nice) scale.nice(); - if (range) scale.range(range); - - applyZero(scale, config); - - return scale; -} +export const updateQuantizeScale = scaleOperator<'quantize'>('domain', 'range', 'nice', 'zero'); export default function createQuantizeScale( config: PickScaleConfigWithoutType<'quantize', Output>, diff --git a/packages/vx-scale/src/scales/squareRoot.ts b/packages/vx-scale/src/scales/squareRoot.ts index 3f661983b..a3238a450 100644 --- a/packages/vx-scale/src/scales/squareRoot.ts +++ b/packages/vx-scale/src/scales/squareRoot.ts @@ -1,15 +1,17 @@ import { scaleSqrt } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToD3Scale } from '../types/Scale'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { updatePowScale } from './power'; +import scaleOperator from '../operators/scaleOperator'; -export function updateSqrtScale( - scale: ScaleTypeToD3Scale['sqrt'], - config: PickScaleConfigWithoutType<'sqrt', Output>, -) { - return updatePowScale(scale, { ...config, exponent: 0.5 }); -} +export const updateSqrtScale = scaleOperator<'sqrt'>( + 'domain', + 'range', + 'clamp', + 'interpolate', + 'nice', + 'round', + 'zero', +); export default function createSqrtScale( config: PickScaleConfigWithoutType<'sqrt', Output>, diff --git a/packages/vx-scale/src/scales/symlog.ts b/packages/vx-scale/src/scales/symlog.ts index 98230eeb8..cac28d055 100644 --- a/packages/vx-scale/src/scales/symlog.ts +++ b/packages/vx-scale/src/scales/symlog.ts @@ -1,26 +1,17 @@ import { scaleSymlog } from 'd3-scale'; import { Value } from '../types/Base'; -import { ScaleTypeToD3Scale } from '../types/Scale'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import applyRound from '../mixins/applyRound'; -import applyZero from '../mixins/applyZero'; +import scaleOperator from '../operators/scaleOperator'; -export function updateSymlogScale( - scale: ScaleTypeToD3Scale['symlog'], - config: PickScaleConfigWithoutType<'symlog', Output>, -) { - const { domain, range, clamp = false, nice = false } = config; - - if (domain) scale.domain(domain); - if (nice) scale.nice(); - if (range) scale.range(range); - - scale.clamp(clamp); - applyRound(scale, config); - applyZero(scale, config); - - return scale; -} +export const updateSymlogScale = scaleOperator<'symlog'>( + 'domain', + 'range', + 'clamp', + 'constant', + 'nice', + 'round', + 'zero', +); export default function createSymlogScale( config: PickScaleConfigWithoutType<'symlog', Output>, diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index 623d06ef0..7690720c3 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -1,22 +1,10 @@ import { scaleThreshold } from 'd3-scale'; import { Value, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale, DefaultThresholdInput } from '../types/Scale'; +import { DefaultThresholdInput } from '../types/Scale'; +import scaleOperator from '../operators/scaleOperator'; -export function updateThresholdScale< - ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, - Output = Value ->( - scale: ScaleTypeToD3Scale['threshold'], - config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>, -) { - const { domain, range } = config; - - if (domain) scale.domain(domain); - if (range) scale.range(range); - - return scale; -} +export const updateThresholdScale = scaleOperator<'threshold'>('domain', 'range'); export default function createThresholdScale< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, diff --git a/packages/vx-scale/src/scales/time.ts b/packages/vx-scale/src/scales/time.ts index 51b846a43..502376de0 100644 --- a/packages/vx-scale/src/scales/time.ts +++ b/packages/vx-scale/src/scales/time.ts @@ -1,26 +1,16 @@ import { scaleTime } from 'd3-scale'; import { Value } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { ScaleTypeToD3Scale } from '../types/Scale'; -import applyInterpolate from '../mixins/applyInterpolate'; -import applyRound from '../mixins/applyRound'; +import scaleOperator from '../operators/scaleOperator'; -export function updateTimeScale( - scale: ScaleTypeToD3Scale['time'], - config: PickScaleConfigWithoutType<'time', Output>, -) { - const { domain, range, clamp = false, nice = false } = config; - - if (domain) scale.domain(domain); - if (nice) scale.nice(); - if (range) scale.range(range); - - scale.clamp(clamp); - applyInterpolate(scale, config); - applyRound(scale, config); - - return scale; -} +export const updateTimeScale = scaleOperator<'time'>( + 'domain', + 'range', + 'clamp', + 'interpolate', + 'nice', + 'round', +); export default function createTimeScale( config: PickScaleConfigWithoutType<'time', Output>, diff --git a/packages/vx-scale/src/scales/utc.ts b/packages/vx-scale/src/scales/utc.ts index 2949c0994..b6f32acf0 100644 --- a/packages/vx-scale/src/scales/utc.ts +++ b/packages/vx-scale/src/scales/utc.ts @@ -1,9 +1,16 @@ import { scaleUtc } from 'd3-scale'; import { Value } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { updateTimeScale } from './time'; +import scaleOperator from '../operators/scaleOperator'; -export const updateUtcScale = updateTimeScale; +export const updateUtcScale = scaleOperator<'utc'>( + 'domain', + 'range', + 'clamp', + 'interpolate', + 'nice', + 'round', +); export default function createUtcScale( config: PickScaleConfigWithoutType<'utc', Output>, From 783e56a46f8a924197d1140c1fdeb08904136e7b Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 18:03:08 -0700 Subject: [PATCH 15/42] test: fix unit test --- packages/vx-axis/test/scales.test.tsx | 12 ++++++++---- .../vx-demo/src/sandboxes/vx-bars/Example.tsx | 6 ++++-- .../vx-demo/src/sandboxes/vx-stats/Example.tsx | 6 ++++-- packages/vx-legend/test/Legend.test.tsx | 3 ++- packages/vx-scale/src/types/ScaleConfig.ts | 4 ---- packages/vx-scale/test/scaleBand.test.ts | 17 +++++++++++++---- packages/vx-stats/src/ViolinPlot.tsx | 7 ++++--- packages/vx-stats/test/BoxPlot.test.tsx | 3 ++- packages/vx-stats/test/ViolinPlot.test.tsx | 3 ++- 9 files changed, 39 insertions(+), 22 deletions(-) diff --git a/packages/vx-axis/test/scales.test.tsx b/packages/vx-axis/test/scales.test.tsx index 0ce6ba82e..979d95c9b 100644 --- a/packages/vx-axis/test/scales.test.tsx +++ b/packages/vx-axis/test/scales.test.tsx @@ -31,7 +31,8 @@ describe('Axis scales', () => { expect( setup( scaleBand({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: ['a', 'b', 'c'], }) as GenericScale, ), @@ -42,7 +43,8 @@ describe('Axis scales', () => { expect( setup( scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }) as GenericScale, ), @@ -53,7 +55,8 @@ describe('Axis scales', () => { expect( setup( scaleLog({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [1, 10, 100, 1000], }) as GenericScale, ), @@ -75,7 +78,8 @@ describe('Axis scales', () => { expect( setup( scalePoint({ - rangeRound: [0, 10], + range: [0, 10], + round: true, domain: ['a', 'b', 'c'], }) as GenericScale, ), diff --git a/packages/vx-demo/src/sandboxes/vx-bars/Example.tsx b/packages/vx-demo/src/sandboxes/vx-bars/Example.tsx index 829449e7f..239ead683 100644 --- a/packages/vx-demo/src/sandboxes/vx-bars/Example.tsx +++ b/packages/vx-demo/src/sandboxes/vx-bars/Example.tsx @@ -27,7 +27,8 @@ export default function Example({ width, height, events = false }: BarsProps) { const xScale = useMemo( () => scaleBand({ - rangeRound: [0, xMax], + range: [0, xMax], + round: true, domain: data.map(getLetter), padding: 0.4, }), @@ -36,7 +37,8 @@ export default function Example({ width, height, events = false }: BarsProps) { const yScale = useMemo( () => scaleLinear({ - rangeRound: [yMax, 0], + range: [yMax, 0], + round: true, domain: [0, Math.max(...data.map(getLetterFrequency))], }), [yMax], diff --git a/packages/vx-demo/src/sandboxes/vx-stats/Example.tsx b/packages/vx-demo/src/sandboxes/vx-stats/Example.tsx index 3eac9d72a..aba10eea7 100644 --- a/packages/vx-demo/src/sandboxes/vx-stats/Example.tsx +++ b/packages/vx-demo/src/sandboxes/vx-stats/Example.tsx @@ -50,7 +50,8 @@ export default withTooltip( // scales const xScale = scaleBand({ - rangeRound: [0, xMax], + range: [0, xMax], + round: true, domain: data.map(x), padding: 0.4, }); @@ -63,7 +64,8 @@ export default withTooltip( const maxYValue = Math.max(...values); const yScale = scaleLinear({ - rangeRound: [yMax, 0], + range: [yMax, 0], + round: true, domain: [minYValue, maxYValue], }); diff --git a/packages/vx-legend/test/Legend.test.tsx b/packages/vx-legend/test/Legend.test.tsx index d47391ea2..8b4dd2211 100644 --- a/packages/vx-legend/test/Legend.test.tsx +++ b/packages/vx-legend/test/Legend.test.tsx @@ -6,7 +6,8 @@ import { Legend, LegendLabel } from '../src'; const defaultProps = { scale: scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }), }; diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index 233f42a19..be1d268f1 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -139,10 +139,6 @@ export interface ScaleTypeToScaleConfig< export type ScaleType = keyof ScaleTypeToScaleConfig; -export type TimeScaleType = 'time' | 'utc'; - -export type DiscreteScaleType = 'ordinal' | 'point' | 'band'; - export type PickScaleConfig< T extends ScaleType, Output = Value, diff --git a/packages/vx-scale/test/scaleBand.test.ts b/packages/vx-scale/test/scaleBand.test.ts index 19d183e83..c28a5cd11 100644 --- a/packages/vx-scale/test/scaleBand.test.ts +++ b/packages/vx-scale/test/scaleBand.test.ts @@ -11,10 +11,19 @@ describe('scaleBand', () => { expect(scale.range()).toEqual(range); }); - test('rangeRound param should set scale range', () => { - const rangeRound = [2, 3] as [number, number]; - const scale = scaleBand({ rangeRound }); - expect(scale.range()).toEqual(rangeRound); + describe('round param should set rounding', () => { + test('round = true', () => { + const scale = scaleBand({ domain: ['a', 'b', 'c'], range: [1.1, 3.5], round: true }); + expect(scale('a')).toEqual(2); + expect(scale('b')).toEqual(2); + expect(scale('c')).toEqual(2); + }); + test('round = false', () => { + const scale = scaleBand({ domain: ['a', 'b', 'c'], range: [1.1, 3.5], round: false }); + expect(scale('a')).toEqual(1.1); + expect(scale('b')).toEqual(1.9); + expect(scale('c')).toEqual(2.7); + }); }); test('domain param should set scale domain', () => { diff --git a/packages/vx-stats/src/ViolinPlot.tsx b/packages/vx-stats/src/ViolinPlot.tsx index 0b3c0ebe2..eea695665 100644 --- a/packages/vx-stats/src/ViolinPlot.tsx +++ b/packages/vx-stats/src/ViolinPlot.tsx @@ -25,8 +25,8 @@ export default function ViolinPlot({ className, data, width = 10, - count = (d: any) => (d && d.count) || 0, - value = (d: any) => (d && d.value) || 0, + count = (d: unknown) => d?.count || 0, + value = (d: unknown) => d?.value || 0, valueScale, horizontal, children, @@ -36,7 +36,8 @@ export default function ViolinPlot({ const center = (horizontal ? top : left) + width / 2; const binCounts = data.map(bin => count(bin)); const widthScale = scaleLinear({ - rangeRound: [0, width / 2], + range: [0, width / 2], + round: true, domain: [0, Math.max(...binCounts)], }); diff --git a/packages/vx-stats/test/BoxPlot.test.tsx b/packages/vx-stats/test/BoxPlot.test.tsx index 9dd207ccf..666674957 100644 --- a/packages/vx-stats/test/BoxPlot.test.tsx +++ b/packages/vx-stats/test/BoxPlot.test.tsx @@ -10,7 +10,8 @@ const { boxPlot: boxPlotData } = computeStats(data); const { min, firstQuartile, median, thirdQuartile, max, outliers } = boxPlotData; const valueScale = scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }); diff --git a/packages/vx-stats/test/ViolinPlot.test.tsx b/packages/vx-stats/test/ViolinPlot.test.tsx index 6d5f501c2..5bcb3a2ff 100644 --- a/packages/vx-stats/test/ViolinPlot.test.tsx +++ b/packages/vx-stats/test/ViolinPlot.test.tsx @@ -9,7 +9,8 @@ const data = [1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 1]; const { binData } = computeStats(data); const valueScale = scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }); From 28a3783d5284ace718661de8d9b051f9fa3fb9f3 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 19:09:19 -0700 Subject: [PATCH 16/42] fix: broken tests --- README.md | 6 ++++-- packages/vx-axis/test/Axis.test.tsx | 6 ++++-- packages/vx-axis/test/AxisBottom.test.tsx | 3 ++- packages/vx-axis/test/AxisLeft.test.tsx | 3 ++- packages/vx-axis/test/AxisRight.test.tsx | 3 ++- packages/vx-axis/test/AxisTop.test.tsx | 3 ++- packages/vx-scale/Readme.md | 6 ++++-- 7 files changed, 20 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 88196af0c..e2eb9678b 100644 --- a/README.md +++ b/README.md @@ -103,12 +103,14 @@ const y = d => +d.frequency * 100; // And then scale the graph by our data const xScale = scaleBand({ - rangeRound: [0, xMax], + range: [0, xMax], + round: true, domain: data.map(x), padding: 0.4, }); const yScale = scaleLinear({ - rangeRound: [yMax, 0], + range: [yMax, 0], + round: true, domain: [0, Math.max(...data.map(y))], }); diff --git a/packages/vx-axis/test/Axis.test.tsx b/packages/vx-axis/test/Axis.test.tsx index fc96501f3..40f49b5ca 100644 --- a/packages/vx-axis/test/Axis.test.tsx +++ b/packages/vx-axis/test/Axis.test.tsx @@ -10,7 +10,8 @@ import { GenericScale } from '../src/types'; const axisProps = { orientation: 'left' as const, scale: scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }) as GenericScale, label: 'test axis', @@ -199,7 +200,8 @@ describe('', () => { const overrideAxisProps = { orientation: 'bottom' as const, scale: scaleBand({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: ['a', 'b'], }), }; diff --git a/packages/vx-axis/test/AxisBottom.test.tsx b/packages/vx-axis/test/AxisBottom.test.tsx index e5c8066d3..1a4752716 100644 --- a/packages/vx-axis/test/AxisBottom.test.tsx +++ b/packages/vx-axis/test/AxisBottom.test.tsx @@ -7,7 +7,8 @@ import { GenericScale } from '../src/types'; const axisProps = { scale: scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }) as GenericScale, }; diff --git a/packages/vx-axis/test/AxisLeft.test.tsx b/packages/vx-axis/test/AxisLeft.test.tsx index f9256a954..e943d3f97 100644 --- a/packages/vx-axis/test/AxisLeft.test.tsx +++ b/packages/vx-axis/test/AxisLeft.test.tsx @@ -7,7 +7,8 @@ import { GenericScale } from '../src/types'; const axisProps = { scale: scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }) as GenericScale, }; diff --git a/packages/vx-axis/test/AxisRight.test.tsx b/packages/vx-axis/test/AxisRight.test.tsx index 07b04aa7f..50dc26d75 100644 --- a/packages/vx-axis/test/AxisRight.test.tsx +++ b/packages/vx-axis/test/AxisRight.test.tsx @@ -7,7 +7,8 @@ import { GenericScale } from '../src/types'; const axisProps = { scale: scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }) as GenericScale, }; diff --git a/packages/vx-axis/test/AxisTop.test.tsx b/packages/vx-axis/test/AxisTop.test.tsx index b26285cf0..74a6b4698 100644 --- a/packages/vx-axis/test/AxisTop.test.tsx +++ b/packages/vx-axis/test/AxisTop.test.tsx @@ -7,7 +7,8 @@ import { GenericScale } from '../src/types'; const axisProps = { scale: scaleLinear({ - rangeRound: [10, 0], + range: [10, 0], + round: true, domain: [0, 10], }) as GenericScale, }; diff --git a/packages/vx-scale/Readme.md b/packages/vx-scale/Readme.md index e3db38533..169541efa 100644 --- a/packages/vx-scale/Readme.md +++ b/packages/vx-scale/Readme.md @@ -33,14 +33,16 @@ const [minY, maxY] = getYMinAndMax(); const xScale = Scale.scaleLinear({ domain: [minX, maxX], // x-coordinate data values - rangeRound: [0, graphWidth], // svg x-coordinates, svg x-coordinates increase left to right + range: [0, graphWidth], // svg x-coordinates, svg x-coordinates increase left to right + round: true, }); const yScale = Scale.scaleLinear({ domain: [minY, maxY], // y-coordinate data values // svg y-coordinates, these increase from top to bottom so we reverse the order // so that minY in data space maps to graphHeight in svg y-coordinate space - rangeRound: [graphHeight, 0], + range: [graphHeight, 0], + round: true, }); // ... From 56e0a4766dd52ce255e79b35640de5555f52d2c8 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 19:55:40 -0700 Subject: [PATCH 17/42] fix: violin type --- packages/vx-stats/src/ViolinPlot.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vx-stats/src/ViolinPlot.tsx b/packages/vx-stats/src/ViolinPlot.tsx index eea695665..fa10fb0cc 100644 --- a/packages/vx-stats/src/ViolinPlot.tsx +++ b/packages/vx-stats/src/ViolinPlot.tsx @@ -25,8 +25,8 @@ export default function ViolinPlot({ className, data, width = 10, - count = (d: unknown) => d?.count || 0, - value = (d: unknown) => d?.value || 0, + count = (d?: { count?: number }) => d?.count || 0, + value = (d?: { value?: number }) => d?.value || 0, valueScale, horizontal, children, From f5fb093b3270eb3f84d423a3cc79a47456663587 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 19:57:02 -0700 Subject: [PATCH 18/42] fix: symlog round --- packages/vx-scale/src/scales/symlog.ts | 1 - packages/vx-scale/src/types/ScaleConfig.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/vx-scale/src/scales/symlog.ts b/packages/vx-scale/src/scales/symlog.ts index cac28d055..0e5f155eb 100644 --- a/packages/vx-scale/src/scales/symlog.ts +++ b/packages/vx-scale/src/scales/symlog.ts @@ -9,7 +9,6 @@ export const updateSymlogScale = scaleOperator<'symlog'>( 'clamp', 'constant', 'nice', - 'round', 'zero', ); diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index be1d268f1..cd8bef744 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -52,7 +52,7 @@ export type SymlogScaleConfig = CreateScaleConfig< 'symlog', ContinuousDomain, Output[], - 'clamp' | 'constant' | 'nice' | 'round' | 'zero' + 'clamp' | 'constant' | 'nice' | 'zero' >; export type QuantileScaleConfig = CreateScaleConfig< From 7f45b41e97909c17a91693831cfa01cd45d40548 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Wed, 22 Jul 2020 20:05:52 -0700 Subject: [PATCH 19/42] docs: update comment --- packages/vx-scale/src/updateScale.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index 12b62a17a..bc0d9b64a 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -158,7 +158,7 @@ function updateScale< if ('clamp' in scaleOut) { // Linear, Time or Utc scales if (scaleOut.ticks()[0] instanceof Date) { - // Time scale + // Time or Utc scales return updateTimeScale( scaleOut as ScaleTime, config as PickScaleConfigWithoutType<'time', Output>, From 41f4229aa51b1471844912306ad83bc945520798 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 09:29:03 -0700 Subject: [PATCH 20/42] fix: update generic --- packages/vx-scale/src/createScale.ts | 18 +++--- packages/vx-scale/src/updateScale.ts | 90 ++++++++++++++++++++++------ 2 files changed, 84 insertions(+), 24 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 3b43182ea..f56bdcd29 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -20,16 +20,20 @@ import createBandScale from './scales/band'; // instead of a union type of all scales. function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput, - Config extends ScaleConfig + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, + Config extends ScaleConfig = ScaleConfig< + Output, + DiscreteInput, + ThresholdInput + > >(config: Config): ScaleConfigToD3Scale; function createScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( config: PickScaleConfigWithoutType<'linear', Output>, ): PickD3Scale<'linear', Output, DiscreteInput, ThresholdInput>; diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index bc0d9b64a..a31a50c8a 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -17,76 +17,132 @@ import { updateLinearScale } from './scales/linear'; // Overload function signature for more strict typing, e.g., // If the scale is a ScaleLinear, the config is a linear config. -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'linear', Output>, config: PickScaleConfigWithoutType<'linear', Output>, ): PickD3Scale<'linear', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'log', Output>, config: PickScaleConfigWithoutType<'log', Output>, ): PickD3Scale<'log', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'pow', Output>, config: PickScaleConfigWithoutType<'pow', Output>, ): PickD3Scale<'pow', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'sqrt', Output>, config: PickScaleConfigWithoutType<'sqrt', Output>, ): PickD3Scale<'sqrt', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'symlog', Output>, config: PickScaleConfigWithoutType<'symlog', Output>, ): PickD3Scale<'symlog', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'time', Output>, config: PickScaleConfigWithoutType<'time', Output>, ): PickD3Scale<'time', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'utc', Output>, config: PickScaleConfigWithoutType<'utc', Output>, ): PickD3Scale<'utc', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'quantile', Output>, config: PickScaleConfigWithoutType<'quantile', Output>, ): PickD3Scale<'quantile', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'quantize', Output>, config: PickScaleConfigWithoutType<'quantize', Output>, ): PickD3Scale<'quantize', Output>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'threshold', Output, StringLike, ThresholdInput>, config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>, ): PickD3Scale<'threshold', Output, StringLike, ThresholdInput>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'ordinal', Output, DiscreteInput>, config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>, ): PickD3Scale<'ordinal', Output, DiscreteInput>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'point', Output, DiscreteInput>, config: PickScaleConfigWithoutType<'point', Output, DiscreteInput>, ): PickD3Scale<'point', Output, DiscreteInput>; -function updateScale( +function updateScale< + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( scale: PickD3Scale<'band', Output, DiscreteInput>, config: PickScaleConfigWithoutType<'band', Output, DiscreteInput>, ): PickD3Scale<'band', Output, DiscreteInput>; function updateScale< - Output extends Value, - DiscreteInput extends StringLike, - ThresholdInput extends DefaultThresholdInput, - Scale extends D3Scale + Output extends Value = Value, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, + Scale extends D3Scale = D3Scale< + Output, + DiscreteInput, + ThresholdInput + > >(scale: Scale, config?: undefined): Scale; // Actual implementation From 8d772a5e8df1a1f9762b9b005efe4ee1d22acd31 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 09:52:29 -0700 Subject: [PATCH 21/42] feat: add inferScaleType and export types --- packages/vx-scale/src/createScale.ts | 2 - packages/vx-scale/src/index.ts | 8 + packages/vx-scale/src/operators/nice.ts | 14 +- packages/vx-scale/src/updateScale.ts | 142 +++++++++--------- packages/vx-scale/src/utils/inferScaleType.ts | 56 +++++++ packages/vx-scale/src/utils/isUtcScale.ts | 14 ++ 6 files changed, 151 insertions(+), 85 deletions(-) create mode 100644 packages/vx-scale/src/utils/inferScaleType.ts create mode 100644 packages/vx-scale/src/utils/isUtcScale.ts diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index f56bdcd29..896eff953 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -78,8 +78,6 @@ function createScale< case 'band': return createBandScale(config); default: - // @ts-ignore - throw new Error(`Invalid scale type: ${config.type}`); } } diff --git a/packages/vx-scale/src/index.ts b/packages/vx-scale/src/index.ts index b6ca39e54..11ca4fdd2 100644 --- a/packages/vx-scale/src/index.ts +++ b/packages/vx-scale/src/index.ts @@ -14,3 +14,11 @@ export { default as scaleSqrt } from './scales/squareRoot'; export { default as scale } from './createScale'; export { default as updateScale } from './updateScale'; +export { default as inferScaleType } from './utils/inferScaleType'; + +// export types +export * from './types/Base'; +export * from './types/Nice'; +export * from './types/Scale'; +export * from './types/ScaleConfig'; +export * from './types/ScaleInterpolate'; diff --git a/packages/vx-scale/src/operators/nice.ts b/packages/vx-scale/src/operators/nice.ts index 39de7a9fa..1dc3c41f9 100644 --- a/packages/vx-scale/src/operators/nice.ts +++ b/packages/vx-scale/src/operators/nice.ts @@ -20,6 +20,7 @@ import { StringLike } from '../types/Base'; import { DefaultThresholdInput, D3Scale } from '../types/Scale'; import { ScaleConfigWithoutType } from '../types/ScaleConfig'; import { NiceTime } from '../types/Nice'; +import isUtcScale from '../utils/isUtcScale'; const localTimeIntervals: { [key in NiceTime]: CountableTimeInterval; @@ -45,19 +46,6 @@ const utcIntervals: { year: utcYear, }; -const TEST_TIME = new Date(Date.UTC(2020, 1, 2, 3, 4, 5)); -const TEST_FORMAT = '%Y-%m-%d %H:%M'; - -/** - * Check if the scale is utc or time scale - * @param scale time or utc scale - */ -function isUtcScale(scale: ScaleTime) { - // The only difference between time and utc scale is - // whether the tick format is utcFormat or timeFormat - return scale.tickFormat(1, TEST_FORMAT)(TEST_TIME) === '2020-02-01 19:04'; -} - export default function applyNice< Output, DiscreteInput extends StringLike, diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index a31a50c8a..9b7d35ec7 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -1,4 +1,3 @@ -import { ScaleTime, ScaleLinear } from 'd3-scale'; import { ScaleConfig, PickScaleConfigWithoutType } from './types/ScaleConfig'; import { DefaultThresholdInput, D3Scale, PickD3Scale } from './types/Scale'; import { StringLike, Value } from './types/Base'; @@ -13,6 +12,9 @@ import { updatePowScale } from './scales/power'; import { updateQuantizeScale } from './scales/quantize'; import { updateTimeScale } from './scales/time'; import { updateLinearScale } from './scales/linear'; +import { updateSqrtScale } from './scales/squareRoot'; +import { updateUtcScale } from './scales/utc'; +import inferScaleType from './utils/inferScaleType'; // Overload function signature for more strict typing, e.g., // If the scale is a ScaleLinear, the config is a linear config. @@ -162,81 +164,81 @@ function updateScale< return scaleOut; } - // Try a sequence of typeguards to figure out the scale type - // and cast the config to correct type. + const type = inferScaleType(scale); + // Function overloading above should ensure the scale and config // are compatible matches. + // Just cast the scale and config to the correct types. - if ('paddingInner' in scaleOut) { - // Band scale - return updateBandScale( - scaleOut, - config as PickScaleConfigWithoutType<'band', Output, DiscreteInput, ThresholdInput>, - ); - } - - if ('padding' in scaleOut) { - // Point scale - return updatePointScale( - scaleOut, - config as PickScaleConfigWithoutType<'point', Output, DiscreteInput, ThresholdInput>, - ); - } - - if ('unknown' in scaleOut) { - // Ordinal scale - return updateOrdinalScale( - scaleOut, - config as PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput, ThresholdInput>, - ); - } - - if ('quantiles' in scaleOut) { - // Quantile scale - return updateQuantileScale(scaleOut, config as PickScaleConfigWithoutType<'quantile', Output>); - } - - if ('base' in scaleOut) { - // Log scale - return updateLogScale(scaleOut, config as PickScaleConfigWithoutType<'log', Output>); - } - - if ('exponent' in scaleOut) { - // Pow or Sqrt scale - return updatePowScale(scaleOut, config as PickScaleConfigWithoutType<'pow', Output>); - } - - if ('constant' in scaleOut) { - // Symlog scale - return updateSymlogScale(scaleOut, config as PickScaleConfigWithoutType<'symlog', Output>); - } - - if ('clamp' in scaleOut) { - // Linear, Time or Utc scales - if (scaleOut.ticks()[0] instanceof Date) { - // Time or Utc scales + switch (type) { + case 'linear': + return updateLinearScale( + scaleOut as PickD3Scale<'linear', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'linear', Output, DiscreteInput, ThresholdInput>, + ); + case 'log': + return updateLogScale( + scaleOut as PickD3Scale<'log', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'log', Output, DiscreteInput, ThresholdInput>, + ); + case 'pow': + return updatePowScale( + scaleOut as PickD3Scale<'pow', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'pow', Output, DiscreteInput, ThresholdInput>, + ); + case 'sqrt': + return updateSqrtScale( + scaleOut as PickD3Scale<'sqrt', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'sqrt', Output, DiscreteInput, ThresholdInput>, + ); + case 'symlog': + return updateSymlogScale( + scaleOut as PickD3Scale<'symlog', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'symlog', Output, DiscreteInput, ThresholdInput>, + ); + case 'time': return updateTimeScale( - scaleOut as ScaleTime, - config as PickScaleConfigWithoutType<'time', Output>, + scaleOut as PickD3Scale<'time', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'time', Output, DiscreteInput, ThresholdInput>, ); - } - // Linear scale - return updateLinearScale( - scaleOut as ScaleLinear, - config as PickScaleConfigWithoutType<'linear', Output>, - ); - } - - if ('nice' in scaleOut) { - // Symlog scale - return updateQuantizeScale(scaleOut, config as PickScaleConfigWithoutType<'quantize', Output>); + case 'utc': + return updateUtcScale( + scaleOut as PickD3Scale<'utc', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'utc', Output, DiscreteInput, ThresholdInput>, + ); + case 'quantile': + return updateQuantileScale( + scaleOut as PickD3Scale<'quantile', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'quantile', Output, DiscreteInput, ThresholdInput>, + ); + case 'quantize': + return updateQuantizeScale( + scaleOut as PickD3Scale<'quantize', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'quantize', Output, DiscreteInput, ThresholdInput>, + ); + case 'threshold': + return updateThresholdScale( + scaleOut as PickD3Scale<'threshold', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'threshold', Output, DiscreteInput, ThresholdInput>, + ); + case 'ordinal': + return updateOrdinalScale( + scaleOut as PickD3Scale<'ordinal', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput, ThresholdInput>, + ); + case 'point': + return updatePointScale( + scaleOut as PickD3Scale<'point', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'point', Output, DiscreteInput, ThresholdInput>, + ); + case 'band': + return updateBandScale( + scaleOut as PickD3Scale<'band', Output, DiscreteInput, ThresholdInput>, + config as PickScaleConfigWithoutType<'band', Output, DiscreteInput, ThresholdInput>, + ); + default: + return scaleOut; } - - // The last type remaining is Threshold scale - return updateThresholdScale( - scaleOut, - config as PickScaleConfigWithoutType<'threshold', Output, DiscreteInput, ThresholdInput>, - ); } export default updateScale; diff --git a/packages/vx-scale/src/utils/inferScaleType.ts b/packages/vx-scale/src/utils/inferScaleType.ts new file mode 100644 index 000000000..fb82d660c --- /dev/null +++ b/packages/vx-scale/src/utils/inferScaleType.ts @@ -0,0 +1,56 @@ +import { ScaleTime } from 'd3-scale'; +import { Value, StringLike } from '../types/Base'; +import { DefaultThresholdInput, D3Scale } from '../types/Scale'; +import { ScaleType } from '../types/ScaleConfig'; +import isUtcScale from './isUtcScale'; + +export default function inferScaleType< + Output extends Value, + DiscreteInput extends StringLike, + ThresholdInput extends DefaultThresholdInput +>(scale: D3Scale): ScaleType { + // Try a sequence of typeguards to figure out the scale type + + if ('paddingInner' in scale) { + return 'band'; + } + + if ('padding' in scale) { + return 'point'; + } + + if ('unknown' in scale) { + return 'ordinal'; + } + + if ('quantiles' in scale) { + return 'quantile'; + } + + if ('base' in scale) { + return 'log'; + } + + if ('exponent' in scale) { + return scale.exponent() === 0.5 ? 'sqrt' : 'pow'; + } + + if ('constant' in scale) { + return 'symlog'; + } + + if ('clamp' in scale) { + // Linear, Time or Utc scales + if (scale.ticks()[0] instanceof Date) { + return isUtcScale(scale as ScaleTime) ? 'utc' : 'time'; + } + return 'linear'; + } + + if ('nice' in scale) { + return 'quantize'; + } + + // The last type remaining is Threshold scale + return 'threshold'; +} diff --git a/packages/vx-scale/src/utils/isUtcScale.ts b/packages/vx-scale/src/utils/isUtcScale.ts new file mode 100644 index 000000000..1c8623159 --- /dev/null +++ b/packages/vx-scale/src/utils/isUtcScale.ts @@ -0,0 +1,14 @@ +import { ScaleTime } from 'd3-scale'; + +const TEST_TIME = new Date(Date.UTC(2020, 1, 2, 3, 4, 5)); +const TEST_FORMAT = '%Y-%m-%d %H:%M'; + +/** + * Check if the scale is utc or time scale + * @param scale time or utc scale + */ +export default function isUtcScale(scale: ScaleTime) { + // The only difference between time and utc scale is + // whether the tick format function is utcFormat or timeFormat + return scale.tickFormat(1, TEST_FORMAT)(TEST_TIME) === '2020-02-01 19:04'; +} From c948e0e61adc10d006376d17f86fb61c9cf72a21 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 10:25:04 -0700 Subject: [PATCH 22/42] fix: domain and range --- packages/vx-scale/src/operators/domain.ts | 12 ++++++++++-- packages/vx-scale/src/operators/range.ts | 9 +++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/vx-scale/src/operators/domain.ts b/packages/vx-scale/src/operators/domain.ts index 7d79cf51d..1213a58e3 100644 --- a/packages/vx-scale/src/operators/domain.ts +++ b/packages/vx-scale/src/operators/domain.ts @@ -11,7 +11,15 @@ export default function applyDomain< config: ScaleConfigWithoutType, ) { if (config.domain) { - // @ts-ignore - scale.domain(config.domain); + if ('padding' in scale || 'unknown' in scale) { + // ordinal, point, band scales + scale.domain(config.domain as DiscreteInput[]); + } else if ('nice' in scale) { + // continuous input scales + scale.domain(config.domain as number[] | Date[]); + } else { + // threshold scale + scale.domain(config.domain as ThresholdInput[]); + } } } diff --git a/packages/vx-scale/src/operators/range.ts b/packages/vx-scale/src/operators/range.ts index a48d7f906..b3b39eee6 100644 --- a/packages/vx-scale/src/operators/range.ts +++ b/packages/vx-scale/src/operators/range.ts @@ -11,7 +11,12 @@ export default function applyRange< config: ScaleConfigWithoutType, ) { if (config.range) { - // @ts-ignore - scale.range(config.range); + if ('padding' in scale) { + // point and band scales + scale.range(config.range as [number, number]); + } else { + // the rest + scale.range(config.range as Output[]); + } } } From 6ef370dcd1724579867734d4cee25c53f29f7eb2 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 12:31:04 -0700 Subject: [PATCH 23/42] test: add unit tests --- packages/vx-scale/src/utils/inferScaleType.ts | 11 ++-- packages/vx-scale/src/utils/isUtcScale.ts | 10 +++- .../test/utils/inferScaleType.test.ts | 59 +++++++++++++++++++ .../vx-scale/test/utils/isUtcScale.test.ts | 13 ++++ 4 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 packages/vx-scale/test/utils/inferScaleType.test.ts create mode 100644 packages/vx-scale/test/utils/isUtcScale.test.ts diff --git a/packages/vx-scale/src/utils/inferScaleType.ts b/packages/vx-scale/src/utils/inferScaleType.ts index fb82d660c..b14004c33 100644 --- a/packages/vx-scale/src/utils/inferScaleType.ts +++ b/packages/vx-scale/src/utils/inferScaleType.ts @@ -19,10 +19,6 @@ export default function inferScaleType< return 'point'; } - if ('unknown' in scale) { - return 'ordinal'; - } - if ('quantiles' in scale) { return 'quantile'; } @@ -51,6 +47,9 @@ export default function inferScaleType< return 'quantize'; } - // The last type remaining is Threshold scale - return 'threshold'; + if ('invertExtent' in scale) { + return 'threshold'; + } + + return 'ordinal'; } diff --git a/packages/vx-scale/src/utils/isUtcScale.ts b/packages/vx-scale/src/utils/isUtcScale.ts index 1c8623159..dd24ba34f 100644 --- a/packages/vx-scale/src/utils/isUtcScale.ts +++ b/packages/vx-scale/src/utils/isUtcScale.ts @@ -3,12 +3,18 @@ import { ScaleTime } from 'd3-scale'; const TEST_TIME = new Date(Date.UTC(2020, 1, 2, 3, 4, 5)); const TEST_FORMAT = '%Y-%m-%d %H:%M'; +export function isLocalTimeInUtc() { + return TEST_TIME.getTimezoneOffset() === 0; +} + /** - * Check if the scale is utc or time scale + * Check if the scale is UTC or Time scale + * When local time is equal to UTC, always return true * @param scale time or utc scale */ export default function isUtcScale(scale: ScaleTime) { // The only difference between time and utc scale is // whether the tick format function is utcFormat or timeFormat - return scale.tickFormat(1, TEST_FORMAT)(TEST_TIME) === '2020-02-01 19:04'; + const output = scale.tickFormat(1, TEST_FORMAT)(TEST_TIME); + return output === '2020-02-02 03:04'; } diff --git a/packages/vx-scale/test/utils/inferScaleType.test.ts b/packages/vx-scale/test/utils/inferScaleType.test.ts new file mode 100644 index 000000000..a97820428 --- /dev/null +++ b/packages/vx-scale/test/utils/inferScaleType.test.ts @@ -0,0 +1,59 @@ +import { + scaleLinear, + scaleLog, + scalePow, + scaleSqrt, + scaleSymlog, + scaleTime, + scaleUtc, + scaleQuantile, + scaleQuantize, + scaleThreshold, + scaleOrdinal, + scalePoint, + scaleBand, +} from 'd3-scale'; +import inferScaleType from '../../src/utils/inferScaleType'; +import { isLocalTimeInUtc } from '../../src/utils/isUtcScale'; + +describe('inferScaleType(scale)', () => { + it('linear scale', () => { + expect(inferScaleType(scaleLinear())).toEqual('linear'); + }); + it('log scale', () => { + expect(inferScaleType(scaleLog())).toEqual('log'); + }); + it('pow scale', () => { + expect(inferScaleType(scalePow())).toEqual('pow'); + }); + it('sqrt scale', () => { + expect(inferScaleType(scaleSqrt())).toEqual('sqrt'); + }); + it('symlog scale', () => { + expect(inferScaleType(scaleSymlog())).toEqual('symlog'); + }); + it('time scale', () => { + expect(inferScaleType(scaleTime())).toEqual(isLocalTimeInUtc() ? 'utc' : 'time'); + }); + it('utc scale', () => { + expect(inferScaleType(scaleUtc())).toEqual('utc'); + }); + it('quantile scale', () => { + expect(inferScaleType(scaleQuantile())).toEqual('quantile'); + }); + it('quantize scale', () => { + expect(inferScaleType(scaleQuantize())).toEqual('quantize'); + }); + it('threshold scale', () => { + expect(inferScaleType(scaleThreshold())).toEqual('threshold'); + }); + it('ordinal scale', () => { + expect(inferScaleType(scaleOrdinal())).toEqual('ordinal'); + }); + it('point scale', () => { + expect(inferScaleType(scalePoint())).toEqual('point'); + }); + it('band scale', () => { + expect(inferScaleType(scaleBand())).toEqual('band'); + }); +}); diff --git a/packages/vx-scale/test/utils/isUtcScale.test.ts b/packages/vx-scale/test/utils/isUtcScale.test.ts new file mode 100644 index 000000000..f82d70356 --- /dev/null +++ b/packages/vx-scale/test/utils/isUtcScale.test.ts @@ -0,0 +1,13 @@ +import { scaleUtc, scaleTime } from 'd3-scale'; +import isUtcScale, { isLocalTimeInUtc } from '../../src/utils/isUtcScale'; + +describe('isUtcScale(scale)', () => { + it('returns true for utc scale', () => { + expect(isUtcScale(scaleUtc())).toEqual(true); + }); + it('returns false for time scale (when local time is not UTC)', () => { + // This check only works if local and utc time are different. + // If the local time is equal UTC then it also returns true. + expect(isUtcScale(scaleTime())).toEqual(isLocalTimeInUtc()); + }); +}); From bad21bb2e90e7c4a0b248c46a3a0c1f4ee59a50a Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 12:49:08 -0700 Subject: [PATCH 24/42] test: mock timezone --- package.json | 3 ++- packages/vx-scale/src/utils/isUtcScale.ts | 4 ---- .../vx-scale/test/utils/inferScaleType.test.ts | 15 ++++++++++++--- .../vx-scale/test/utils/isUtcScale.test.ts | 18 +++++++++++++----- yarn.lock | 5 +++++ 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 668268fdd..5808934a8 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,8 @@ "react": "^15.0.0-0 || ^16.0.0-0", "react-dom": "^15.0.0-0 || ^16.0.0-0", "react-test-renderer": "^16.8.6", - "regenerator-runtime": "^0.10.5" + "regenerator-runtime": "^0.10.5", + "timezone-mock": "^1.1.0" }, "workspaces": [ "./packages/*" diff --git a/packages/vx-scale/src/utils/isUtcScale.ts b/packages/vx-scale/src/utils/isUtcScale.ts index dd24ba34f..6c4a187d2 100644 --- a/packages/vx-scale/src/utils/isUtcScale.ts +++ b/packages/vx-scale/src/utils/isUtcScale.ts @@ -3,10 +3,6 @@ import { ScaleTime } from 'd3-scale'; const TEST_TIME = new Date(Date.UTC(2020, 1, 2, 3, 4, 5)); const TEST_FORMAT = '%Y-%m-%d %H:%M'; -export function isLocalTimeInUtc() { - return TEST_TIME.getTimezoneOffset() === 0; -} - /** * Check if the scale is UTC or Time scale * When local time is equal to UTC, always return true diff --git a/packages/vx-scale/test/utils/inferScaleType.test.ts b/packages/vx-scale/test/utils/inferScaleType.test.ts index a97820428..3d5c441ac 100644 --- a/packages/vx-scale/test/utils/inferScaleType.test.ts +++ b/packages/vx-scale/test/utils/inferScaleType.test.ts @@ -13,8 +13,8 @@ import { scalePoint, scaleBand, } from 'd3-scale'; +import TimezoneMock from 'timezone-mock'; import inferScaleType from '../../src/utils/inferScaleType'; -import { isLocalTimeInUtc } from '../../src/utils/isUtcScale'; describe('inferScaleType(scale)', () => { it('linear scale', () => { @@ -32,8 +32,17 @@ describe('inferScaleType(scale)', () => { it('symlog scale', () => { expect(inferScaleType(scaleSymlog())).toEqual('symlog'); }); - it('time scale', () => { - expect(inferScaleType(scaleTime())).toEqual(isLocalTimeInUtc() ? 'utc' : 'time'); + describe('time scale', () => { + it('returns time when local time is not UTC', () => { + TimezoneMock.register('US/Pacific'); + expect(inferScaleType(scaleTime())).toEqual('time'); + TimezoneMock.unregister(); + }); + it('returns utc when local time is UTC', () => { + TimezoneMock.register('UTC'); + expect(inferScaleType(scaleTime())).toEqual('utc'); + TimezoneMock.unregister(); + }); }); it('utc scale', () => { expect(inferScaleType(scaleUtc())).toEqual('utc'); diff --git a/packages/vx-scale/test/utils/isUtcScale.test.ts b/packages/vx-scale/test/utils/isUtcScale.test.ts index f82d70356..bc1e3f335 100644 --- a/packages/vx-scale/test/utils/isUtcScale.test.ts +++ b/packages/vx-scale/test/utils/isUtcScale.test.ts @@ -1,13 +1,21 @@ import { scaleUtc, scaleTime } from 'd3-scale'; -import isUtcScale, { isLocalTimeInUtc } from '../../src/utils/isUtcScale'; +import TimezoneMock from 'timezone-mock'; +import isUtcScale from '../../src/utils/isUtcScale'; describe('isUtcScale(scale)', () => { it('returns true for utc scale', () => { expect(isUtcScale(scaleUtc())).toEqual(true); }); - it('returns false for time scale (when local time is not UTC)', () => { - // This check only works if local and utc time are different. - // If the local time is equal UTC then it also returns true. - expect(isUtcScale(scaleTime())).toEqual(isLocalTimeInUtc()); + describe('for time scale', () => { + it('returns false when local time is not UTC', () => { + TimezoneMock.register('US/Pacific'); + expect(isUtcScale(scaleTime())).toEqual(false); + TimezoneMock.unregister(); + }); + it('returns true when local time is UTC', () => { + TimezoneMock.register('UTC'); + expect(isUtcScale(scaleTime())).toEqual(true); + TimezoneMock.unregister(); + }); }); }); diff --git a/yarn.lock b/yarn.lock index 68677b894..ecb1a5172 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12969,6 +12969,11 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +timezone-mock@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/timezone-mock/-/timezone-mock-1.1.0.tgz#ac402345b42721538131368876cc17cfbb849da3" + integrity sha512-YBUMlri3qt6yh07Q+zEr7boXLaumRnUA96Y/NJxaapW7BcR9GRp39nyg5XrRm1+h2q0L5LIOzHt78uHeBntdaA== + timsort@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" From a0a24c204e31b8f0a2493a34d64f71229d12e7f2 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 13:24:03 -0700 Subject: [PATCH 25/42] fix: rename export and remove extends value --- packages/vx-scale/src/createScale.ts | 6 +-- packages/vx-scale/src/index.ts | 2 +- packages/vx-scale/src/updateScale.ts | 30 +++++------ packages/vx-scale/src/utils/inferScaleType.ts | 4 +- packages/vx-scale/test/scaleLinear.test.ts | 52 ++++++++++++++++++- 5 files changed, 71 insertions(+), 23 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 896eff953..003561bba 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -20,7 +20,7 @@ import createBandScale from './scales/band'; // instead of a union type of all scales. function createScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Config extends ScaleConfig = ScaleConfig< @@ -31,7 +31,7 @@ function createScale< >(config: Config): ScaleConfigToD3Scale; function createScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -41,7 +41,7 @@ function createScale< // Actual implementation function createScale< - Output extends Value, + Output, DiscreteInput extends StringLike, ThresholdInput extends DefaultThresholdInput >( diff --git a/packages/vx-scale/src/index.ts b/packages/vx-scale/src/index.ts index 11ca4fdd2..aa0a70da5 100644 --- a/packages/vx-scale/src/index.ts +++ b/packages/vx-scale/src/index.ts @@ -12,7 +12,7 @@ export { default as scaleSymlog } from './scales/symlog'; export { default as scaleThreshold } from './scales/threshold'; export { default as scaleSqrt } from './scales/squareRoot'; -export { default as scale } from './createScale'; +export { default as createScale } from './createScale'; export { default as updateScale } from './updateScale'; export { default as inferScaleType } from './utils/inferScaleType'; diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index 9b7d35ec7..8277f4280 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -20,7 +20,7 @@ import inferScaleType from './utils/inferScaleType'; // If the scale is a ScaleLinear, the config is a linear config. function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -29,7 +29,7 @@ function updateScale< ): PickD3Scale<'linear', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -38,7 +38,7 @@ function updateScale< ): PickD3Scale<'log', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -47,7 +47,7 @@ function updateScale< ): PickD3Scale<'pow', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -56,7 +56,7 @@ function updateScale< ): PickD3Scale<'sqrt', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -65,7 +65,7 @@ function updateScale< ): PickD3Scale<'symlog', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -74,7 +74,7 @@ function updateScale< ): PickD3Scale<'time', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -83,7 +83,7 @@ function updateScale< ): PickD3Scale<'utc', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -92,7 +92,7 @@ function updateScale< ): PickD3Scale<'quantile', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -101,7 +101,7 @@ function updateScale< ): PickD3Scale<'quantize', Output>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -110,7 +110,7 @@ function updateScale< ): PickD3Scale<'threshold', Output, StringLike, ThresholdInput>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -119,7 +119,7 @@ function updateScale< ): PickD3Scale<'ordinal', Output, DiscreteInput>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -128,7 +128,7 @@ function updateScale< ): PickD3Scale<'point', Output, DiscreteInput>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -137,7 +137,7 @@ function updateScale< ): PickD3Scale<'band', Output, DiscreteInput>; function updateScale< - Output extends Value = Value, + Output = Value, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Scale extends D3Scale = D3Scale< @@ -150,7 +150,7 @@ function updateScale< // Actual implementation function updateScale< - Output extends Value, + Output, DiscreteInput extends StringLike, ThresholdInput extends DefaultThresholdInput >( diff --git a/packages/vx-scale/src/utils/inferScaleType.ts b/packages/vx-scale/src/utils/inferScaleType.ts index b14004c33..146e0aa16 100644 --- a/packages/vx-scale/src/utils/inferScaleType.ts +++ b/packages/vx-scale/src/utils/inferScaleType.ts @@ -1,11 +1,11 @@ import { ScaleTime } from 'd3-scale'; -import { Value, StringLike } from '../types/Base'; +import { StringLike } from '../types/Base'; import { DefaultThresholdInput, D3Scale } from '../types/Scale'; import { ScaleType } from '../types/ScaleConfig'; import isUtcScale from './isUtcScale'; export default function inferScaleType< - Output extends Value, + Output, DiscreteInput extends StringLike, ThresholdInput extends DefaultThresholdInput >(scale: D3Scale): ScaleType { diff --git a/packages/vx-scale/test/scaleLinear.test.ts b/packages/vx-scale/test/scaleLinear.test.ts index 8c9b87a39..05d9eeda1 100644 --- a/packages/vx-scale/test/scaleLinear.test.ts +++ b/packages/vx-scale/test/scaleLinear.test.ts @@ -1,7 +1,55 @@ import { scaleLinear } from '../src'; -describe('scaleLinear', () => { - test('it should be defined', () => { +describe('scaleLinear()', () => { + it('should be defined', () => { expect(scaleLinear).toBeDefined(); }); + it('set domain', () => { + const domain = [1, 2]; + expect(scaleLinear({ domain: [1, 2] }).domain()).toEqual(domain); + }); + it('set range', () => { + const range = [1, 2]; + expect(scaleLinear({ range: [1, 2] }).range()).toEqual(range); + }); + describe('set clamp', () => { + it('true', () => { + const scale = scaleLinear({ clamp: true }); + expect(scale(10)).toEqual(1); + }); + it('false', () => { + const scale = scaleLinear({ clamp: false }); + expect(scale(10)).toEqual(10); + }); + }); + it('set (color) interpolate', () => { + const scale = scaleLinear({ + domain: [0, 10], + range: ['#ff0000', '#000000'], + interpolate: 'lab', + }); + expect(scale(5)).toEqual('rgb(122, 27, 11)'); + }); + describe('set nice', () => { + it('true', () => { + const scale = scaleLinear({ domain: [0.1, 0.91], nice: true }); + expect(scale.domain()).toEqual([0.1, 1]); + }); + it('false', () => { + const scale = scaleLinear({ domain: [0.1, 0.91], nice: false }); + expect(scale.domain()).toEqual([0.1, 0.91]); + }); + }); + describe('set round', () => { + it('true', () => { + const scale = scaleLinear({ domain: [0, 10], range: [0, 10], round: true }); + expect(scale(2.2)).toEqual(2); + expect(scale(2.6)).toEqual(3); + }); + it('false', () => { + const scale = scaleLinear({ domain: [0, 10], range: [0, 10], round: false }); + expect(scale(2.2)).toEqual(2.2); + expect(scale(2.6)).toEqual(2.6); + }); + }); }); From deb50d697b445a3efb5f0f684b371373bb267e86 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 13:26:35 -0700 Subject: [PATCH 26/42] refactor: rename value to default output --- packages/vx-scale/src/createScale.ts | 6 +-- .../vx-scale/src/operators/scaleOperator.ts | 4 +- packages/vx-scale/src/scales/band.ts | 4 +- packages/vx-scale/src/scales/linear.ts | 4 +- packages/vx-scale/src/scales/log.ts | 4 +- packages/vx-scale/src/scales/ordinal.ts | 4 +- packages/vx-scale/src/scales/point.ts | 4 +- packages/vx-scale/src/scales/power.ts | 4 +- packages/vx-scale/src/scales/quantile.ts | 4 +- packages/vx-scale/src/scales/quantize.ts | 4 +- packages/vx-scale/src/scales/squareRoot.ts | 4 +- packages/vx-scale/src/scales/symlog.ts | 4 +- packages/vx-scale/src/scales/threshold.ts | 4 +- packages/vx-scale/src/scales/time.ts | 4 +- packages/vx-scale/src/scales/utc.ts | 4 +- packages/vx-scale/src/types/Base.ts | 2 +- packages/vx-scale/src/types/Scale.ts | 8 ++-- packages/vx-scale/src/types/ScaleConfig.ts | 38 +++++++++---------- packages/vx-scale/src/updateScale.ts | 30 +++++++-------- 19 files changed, 70 insertions(+), 70 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 003561bba..4db7207a4 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -1,6 +1,6 @@ import { ScaleConfig, ScaleConfigToD3Scale, PickScaleConfigWithoutType } from './types/ScaleConfig'; import { DefaultThresholdInput, PickD3Scale } from './types/Scale'; -import { StringLike, Value } from './types/Base'; +import { StringLike, DefaultOutput } from './types/Base'; import createLinearScale from './scales/linear'; import createLogScale from './scales/log'; import createPowScale from './scales/power'; @@ -20,7 +20,7 @@ import createBandScale from './scales/band'; // instead of a union type of all scales. function createScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Config extends ScaleConfig = ScaleConfig< @@ -31,7 +31,7 @@ function createScale< >(config: Config): ScaleConfigToD3Scale; function createScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( diff --git a/packages/vx-scale/src/operators/scaleOperator.ts b/packages/vx-scale/src/operators/scaleOperator.ts index 77cddf4c3..7c7ea76fa 100644 --- a/packages/vx-scale/src/operators/scaleOperator.ts +++ b/packages/vx-scale/src/operators/scaleOperator.ts @@ -1,6 +1,6 @@ import { DefaultThresholdInput, PickD3Scale } from '../types/Scale'; import { ScaleType, PickScaleConfigWithoutType } from '../types/ScaleConfig'; -import { Value, StringLike } from '../types/Base'; +import { DefaultOutput, StringLike } from '../types/Base'; import domain from './domain'; import range from './range'; import align from './align'; @@ -45,7 +45,7 @@ export default function scaleOperator(...ops: OperatorType[ const selectedOps = orderedOps.filter(o => selection.has(o)); return function applyOperators< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( diff --git a/packages/vx-scale/src/scales/band.ts b/packages/vx-scale/src/scales/band.ts index 5e6bbacb0..05f37745d 100644 --- a/packages/vx-scale/src/scales/band.ts +++ b/packages/vx-scale/src/scales/band.ts @@ -1,5 +1,5 @@ import { scaleBand } from 'd3-scale'; -import { Value, StringLike } from '../types/Base'; +import { DefaultOutput, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -12,7 +12,7 @@ export const updateBandScale = scaleOperator<'band'>( ); export default function createBandScale( - config: PickScaleConfigWithoutType<'band', Value, DiscreteInput>, + config: PickScaleConfigWithoutType<'band', DefaultOutput, DiscreteInput>, ) { return updateBandScale(scaleBand(), config); } diff --git a/packages/vx-scale/src/scales/linear.ts b/packages/vx-scale/src/scales/linear.ts index 4e0858cb7..075443c3f 100644 --- a/packages/vx-scale/src/scales/linear.ts +++ b/packages/vx-scale/src/scales/linear.ts @@ -1,5 +1,5 @@ import { scaleLinear } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -13,7 +13,7 @@ export const updateLinearScale = scaleOperator<'linear'>( 'zero', ); -export default function createLinearScale( +export default function createLinearScale( config: PickScaleConfigWithoutType<'linear', Output>, ) { return updateLinearScale(scaleLinear(), config); diff --git a/packages/vx-scale/src/scales/log.ts b/packages/vx-scale/src/scales/log.ts index 3f8735f13..098c1ed3c 100644 --- a/packages/vx-scale/src/scales/log.ts +++ b/packages/vx-scale/src/scales/log.ts @@ -1,5 +1,5 @@ import { scaleLog } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -13,7 +13,7 @@ export const updateLogScale = scaleOperator<'log'>( 'round', ); -export default function createLogScale( +export default function createLogScale( config: PickScaleConfigWithoutType<'log', Output>, ) { return updateLogScale(scaleLog(), config); diff --git a/packages/vx-scale/src/scales/ordinal.ts b/packages/vx-scale/src/scales/ordinal.ts index cfb2ae22a..79648fda6 100644 --- a/packages/vx-scale/src/scales/ordinal.ts +++ b/packages/vx-scale/src/scales/ordinal.ts @@ -1,5 +1,5 @@ import { scaleOrdinal } from 'd3-scale'; -import { Value, StringLike } from '../types/Base'; +import { DefaultOutput, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -7,7 +7,7 @@ export const updateOrdinalScale = scaleOperator<'ordinal'>('domain', 'range', 'u export default function createOrdinalScale< DiscreteInput extends StringLike = StringLike, - Output = Value + Output = DefaultOutput >(config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>) { return updateOrdinalScale(scaleOrdinal(), config); } diff --git a/packages/vx-scale/src/scales/point.ts b/packages/vx-scale/src/scales/point.ts index 8872345b1..7fd4a3287 100644 --- a/packages/vx-scale/src/scales/point.ts +++ b/packages/vx-scale/src/scales/point.ts @@ -1,5 +1,5 @@ import { scalePoint } from 'd3-scale'; -import { Value, StringLike } from '../types/Base'; +import { DefaultOutput, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -12,7 +12,7 @@ export const updatePointScale = scaleOperator<'point'>( ); export default function createPointScale( - config: PickScaleConfigWithoutType<'point', Value, DiscreteInput>, + config: PickScaleConfigWithoutType<'point', DefaultOutput, DiscreteInput>, ) { return updatePointScale(scalePoint(), config); } diff --git a/packages/vx-scale/src/scales/power.ts b/packages/vx-scale/src/scales/power.ts index 9c56bab14..ac7c81d5a 100644 --- a/packages/vx-scale/src/scales/power.ts +++ b/packages/vx-scale/src/scales/power.ts @@ -1,5 +1,5 @@ import { scalePow } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -14,7 +14,7 @@ export const updatePowScale = scaleOperator<'pow'>( 'zero', ); -export default function createPowScale( +export default function createPowScale( config: PickScaleConfigWithoutType<'pow', Output>, ) { return updatePowScale(scalePow(), config); diff --git a/packages/vx-scale/src/scales/quantile.ts b/packages/vx-scale/src/scales/quantile.ts index b9405a844..cb7cb463c 100644 --- a/packages/vx-scale/src/scales/quantile.ts +++ b/packages/vx-scale/src/scales/quantile.ts @@ -1,11 +1,11 @@ import { scaleQuantile } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; export const updateQuantileScale = scaleOperator<'quantile'>('domain', 'range'); -export default function createQuantileScale( +export default function createQuantileScale( config: PickScaleConfigWithoutType<'quantile', Output>, ) { return updateQuantileScale(scaleQuantile(), config); diff --git a/packages/vx-scale/src/scales/quantize.ts b/packages/vx-scale/src/scales/quantize.ts index 7cdff6a43..8c799abed 100644 --- a/packages/vx-scale/src/scales/quantize.ts +++ b/packages/vx-scale/src/scales/quantize.ts @@ -1,11 +1,11 @@ import { scaleQuantize } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; export const updateQuantizeScale = scaleOperator<'quantize'>('domain', 'range', 'nice', 'zero'); -export default function createQuantizeScale( +export default function createQuantizeScale( config: PickScaleConfigWithoutType<'quantize', Output>, ) { return updateQuantizeScale(scaleQuantize(), config); diff --git a/packages/vx-scale/src/scales/squareRoot.ts b/packages/vx-scale/src/scales/squareRoot.ts index a3238a450..f5305cbdf 100644 --- a/packages/vx-scale/src/scales/squareRoot.ts +++ b/packages/vx-scale/src/scales/squareRoot.ts @@ -1,5 +1,5 @@ import { scaleSqrt } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -13,7 +13,7 @@ export const updateSqrtScale = scaleOperator<'sqrt'>( 'zero', ); -export default function createSqrtScale( +export default function createSqrtScale( config: PickScaleConfigWithoutType<'sqrt', Output>, ) { return updateSqrtScale(scaleSqrt(), config); diff --git a/packages/vx-scale/src/scales/symlog.ts b/packages/vx-scale/src/scales/symlog.ts index 0e5f155eb..6ba9f8c0c 100644 --- a/packages/vx-scale/src/scales/symlog.ts +++ b/packages/vx-scale/src/scales/symlog.ts @@ -1,5 +1,5 @@ import { scaleSymlog } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -12,7 +12,7 @@ export const updateSymlogScale = scaleOperator<'symlog'>( 'zero', ); -export default function createSymlogScale( +export default function createSymlogScale( config: PickScaleConfigWithoutType<'symlog', Output>, ) { return updateSymlogScale(scaleSymlog(), config); diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index 7690720c3..4c29f9777 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -1,5 +1,5 @@ import { scaleThreshold } from 'd3-scale'; -import { Value, StringLike } from '../types/Base'; +import { DefaultOutput, StringLike } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import { DefaultThresholdInput } from '../types/Scale'; import scaleOperator from '../operators/scaleOperator'; @@ -8,7 +8,7 @@ export const updateThresholdScale = scaleOperator<'threshold'>('domain', 'range' export default function createThresholdScale< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, - Output = Value + Output = DefaultOutput >(config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>) { return updateThresholdScale(scaleThreshold(), config); } diff --git a/packages/vx-scale/src/scales/time.ts b/packages/vx-scale/src/scales/time.ts index 502376de0..057263b38 100644 --- a/packages/vx-scale/src/scales/time.ts +++ b/packages/vx-scale/src/scales/time.ts @@ -1,5 +1,5 @@ import { scaleTime } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -12,7 +12,7 @@ export const updateTimeScale = scaleOperator<'time'>( 'round', ); -export default function createTimeScale( +export default function createTimeScale( config: PickScaleConfigWithoutType<'time', Output>, ) { return updateTimeScale(scaleTime(), config); diff --git a/packages/vx-scale/src/scales/utc.ts b/packages/vx-scale/src/scales/utc.ts index b6f32acf0..8b349eeac 100644 --- a/packages/vx-scale/src/scales/utc.ts +++ b/packages/vx-scale/src/scales/utc.ts @@ -1,5 +1,5 @@ import { scaleUtc } from 'd3-scale'; -import { Value } from '../types/Base'; +import { DefaultOutput } from '../types/Base'; import { PickScaleConfigWithoutType } from '../types/ScaleConfig'; import scaleOperator from '../operators/scaleOperator'; @@ -12,7 +12,7 @@ export const updateUtcScale = scaleOperator<'utc'>( 'round', ); -export default function createUtcScale( +export default function createUtcScale( config: PickScaleConfigWithoutType<'utc', Output>, ) { return updateUtcScale(scaleUtc(), config); diff --git a/packages/vx-scale/src/types/Base.ts b/packages/vx-scale/src/types/Base.ts index be4fa3bcf..ae2fb3a51 100644 --- a/packages/vx-scale/src/types/Base.ts +++ b/packages/vx-scale/src/types/Base.ts @@ -5,7 +5,7 @@ export type NumberLike = { valueOf(): number }; export type StringLike = { toString(): string }; /** Possible values */ -export type Value = number | string | boolean | null; +export type DefaultOutput = number | string | boolean | null; /** Union types of all values from a map type */ export type ValueOf = T[keyof T]; diff --git a/packages/vx-scale/src/types/Scale.ts b/packages/vx-scale/src/types/Scale.ts index f707ad2b7..c57221196 100644 --- a/packages/vx-scale/src/types/Scale.ts +++ b/packages/vx-scale/src/types/Scale.ts @@ -11,7 +11,7 @@ import { ScaleBand, ScaleSymLog, } from 'd3-scale'; -import { StringLike, Value, ValueOf } from './Base'; +import { StringLike, DefaultOutput, ValueOf } from './Base'; export type DefaultThresholdInput = number | string | Date; @@ -22,7 +22,7 @@ export type DefaultThresholdInput = number | string | Date; * @type `DiscreteInput`: Input type for ordinal, point and band scales */ export interface ScaleTypeToD3Scale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > { @@ -52,13 +52,13 @@ export interface ScaleTypeToD3Scale< export type PickD3Scale< T extends keyof ScaleTypeToD3Scale, - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf, T>>; export type D3Scale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>; diff --git a/packages/vx-scale/src/types/ScaleConfig.ts b/packages/vx-scale/src/types/ScaleConfig.ts index cd8bef744..f528187b5 100644 --- a/packages/vx-scale/src/types/ScaleConfig.ts +++ b/packages/vx-scale/src/types/ScaleConfig.ts @@ -1,5 +1,5 @@ import { BaseScaleConfig } from './BaseScaleConfig'; -import { StringLike, Value, ValueOf, NumberLike } from './Base'; +import { StringLike, DefaultOutput, ValueOf, NumberLike } from './Base'; import { NiceTime } from './Nice'; import { DefaultThresholdInput, ScaleTypeToD3Scale } from './Scale'; @@ -20,48 +20,48 @@ type CreateScaleConfig = 'type' | 'domain' | 'range' | Fields >; -export type LinearScaleConfig = CreateScaleConfig< +export type LinearScaleConfig = CreateScaleConfig< 'linear', ContinuousDomain, Output[], 'clamp' | 'interpolate' | 'nice' | 'round' | 'zero' >; -export type LogScaleConfig = CreateScaleConfig< +export type LogScaleConfig = CreateScaleConfig< 'log', ContinuousDomain, Output[], 'base' | 'clamp' | 'interpolate' | 'nice' | 'round' >; -export type PowScaleConfig = CreateScaleConfig< +export type PowScaleConfig = CreateScaleConfig< 'pow', ContinuousDomain, Output[], 'clamp' | 'exponent' | 'interpolate' | 'nice' | 'round' | 'zero' >; -export type SqrtScaleConfig = CreateScaleConfig< +export type SqrtScaleConfig = CreateScaleConfig< 'sqrt', ContinuousDomain, Output[], 'clamp' | 'interpolate' | 'nice' | 'round' | 'zero' >; -export type SymlogScaleConfig = CreateScaleConfig< +export type SymlogScaleConfig = CreateScaleConfig< 'symlog', ContinuousDomain, Output[], 'clamp' | 'constant' | 'nice' | 'zero' >; -export type QuantileScaleConfig = CreateScaleConfig< +export type QuantileScaleConfig = CreateScaleConfig< 'quantile', ContinuousDomain, Output[] >; -export type QuantizeScaleConfig = CreateScaleConfig< +export type QuantizeScaleConfig = CreateScaleConfig< 'quantize', [ContinuousInput, ContinuousInput], Output[], @@ -70,12 +70,12 @@ export type QuantizeScaleConfig = CreateScaleConfig< export type ThresholdScaleConfig< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, - Output = Value + Output = DefaultOutput > = CreateScaleConfig<'threshold', ThresholdInput[], Output[]>; export type OrdinalScaleConfig< DiscreteInput extends StringLike = StringLike, - Output = Value + Output = DefaultOutput > = CreateScaleConfig<'ordinal', DiscreteInput[], Output[], 'unknown'>; export type PointScaleConfig = CreateScaleConfig< @@ -92,7 +92,7 @@ export type BandScaleConfig = Cre 'align' | 'padding' | 'paddingInner' | 'paddingOuter' | 'round' >; -interface TemporalScaleConfig +interface TemporalScaleConfig extends CreateScaleConfig { /** * Extending the domain so that it starts and ends on nice round values. This method typically modifies the scale’s domain, and may only extend the bounds to the nearest round value. Nicing is useful if the domain is computed from data and may be irregular. For example, for a domain of _[0.201479…, 0.996679…]_, a nice domain might be _[0.2, 1.0]_. @@ -107,9 +107,9 @@ interface TemporalScaleConfig nice?: boolean | number | NiceTime | { interval: NiceTime; step: number }; } -export type TimeScaleConfig = TemporalScaleConfig<'time', Output>; +export type TimeScaleConfig = TemporalScaleConfig<'time', Output>; -export type UtcScaleConfig = TemporalScaleConfig<'utc', Output>; +export type UtcScaleConfig = TemporalScaleConfig<'utc', Output>; /** * Map scale type to D3Scale type @@ -118,7 +118,7 @@ export type UtcScaleConfig = TemporalScaleConfig<'utc', Output>; * @type `DiscreteInput`: Input type for ordinal, point and band scales */ export interface ScaleTypeToScaleConfig< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > { @@ -141,7 +141,7 @@ export type ScaleType = keyof ScaleTypeToScaleConfig; export type PickScaleConfig< T extends ScaleType, - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf, T>>; @@ -152,26 +152,26 @@ type OmitType = { export type PickScaleConfigWithoutType< T extends ScaleType, - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>, T>>; export type ScaleConfig< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ValueOf>; export type ScaleConfigWithoutType< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = PickScaleConfigWithoutType; export type ScaleConfigToD3Scale< Config extends ScaleConfig, - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput > = ScaleTypeToD3Scale[Config['type']]; diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index 8277f4280..37419b9bf 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -1,6 +1,6 @@ import { ScaleConfig, PickScaleConfigWithoutType } from './types/ScaleConfig'; import { DefaultThresholdInput, D3Scale, PickD3Scale } from './types/Scale'; -import { StringLike, Value } from './types/Base'; +import { StringLike, DefaultOutput } from './types/Base'; import { updateThresholdScale } from './scales/threshold'; import { updateQuantileScale } from './scales/quantile'; import { updateBandScale } from './scales/band'; @@ -20,7 +20,7 @@ import inferScaleType from './utils/inferScaleType'; // If the scale is a ScaleLinear, the config is a linear config. function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -29,7 +29,7 @@ function updateScale< ): PickD3Scale<'linear', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -38,7 +38,7 @@ function updateScale< ): PickD3Scale<'log', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -47,7 +47,7 @@ function updateScale< ): PickD3Scale<'pow', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -56,7 +56,7 @@ function updateScale< ): PickD3Scale<'sqrt', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -65,7 +65,7 @@ function updateScale< ): PickD3Scale<'symlog', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -74,7 +74,7 @@ function updateScale< ): PickD3Scale<'time', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -83,7 +83,7 @@ function updateScale< ): PickD3Scale<'utc', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -92,7 +92,7 @@ function updateScale< ): PickD3Scale<'quantile', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -101,7 +101,7 @@ function updateScale< ): PickD3Scale<'quantize', Output>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -110,7 +110,7 @@ function updateScale< ): PickD3Scale<'threshold', Output, StringLike, ThresholdInput>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -119,7 +119,7 @@ function updateScale< ): PickD3Scale<'ordinal', Output, DiscreteInput>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -128,7 +128,7 @@ function updateScale< ): PickD3Scale<'point', Output, DiscreteInput>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( @@ -137,7 +137,7 @@ function updateScale< ): PickD3Scale<'band', Output, DiscreteInput>; function updateScale< - Output = Value, + Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Scale extends D3Scale = D3Scale< From aabe8e3089ea8c6392c06067a65193228b32ab69 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 13:31:16 -0700 Subject: [PATCH 27/42] docs: update comment --- packages/vx-scale/src/types/Base.ts | 2 +- packages/vx-scale/src/types/BaseScaleConfig.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/vx-scale/src/types/Base.ts b/packages/vx-scale/src/types/Base.ts index ae2fb3a51..bd721bf85 100644 --- a/packages/vx-scale/src/types/Base.ts +++ b/packages/vx-scale/src/types/Base.ts @@ -4,7 +4,7 @@ export type NumberLike = { valueOf(): number }; /** A value that has .toString() function */ export type StringLike = { toString(): string }; -/** Possible values */ +/** Default output type */ export type DefaultOutput = number | string | boolean | null; /** Union types of all values from a map type */ diff --git a/packages/vx-scale/src/types/BaseScaleConfig.ts b/packages/vx-scale/src/types/BaseScaleConfig.ts index ad499858f..319eca6ea 100644 --- a/packages/vx-scale/src/types/BaseScaleConfig.ts +++ b/packages/vx-scale/src/types/BaseScaleConfig.ts @@ -66,6 +66,10 @@ export interface BaseScaleConfig { nice?: boolean | number; /** + * For band scale, shortcut for setting `paddingInner` and `paddingOuter` to the same value. + * + * For point scale, the outer padding (spacing) at the ends of the range. + * This is similar to band scale's `paddingOuter`. * * @minimum 0 */ @@ -74,8 +78,6 @@ export interface BaseScaleConfig { /** * The inner padding (spacing) within each band step of band scales, as a fraction of the step size. This value must lie in the range [0,1]. * - * For point scale, this property is invalid as point scales do not have internal band widths (only step sizes between bands). - * * @minimum 0 * @maximum 1 */ From 32cee04dcb89d4e20c40805cc605924a91c650af Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 13:37:45 -0700 Subject: [PATCH 28/42] fix: better undefined check --- packages/vx-scale/src/operators/interpolate.ts | 6 +++++- packages/vx-scale/src/operators/nice.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/vx-scale/src/operators/interpolate.ts b/packages/vx-scale/src/operators/interpolate.ts index 18f8188fa..0d80ff62b 100644 --- a/packages/vx-scale/src/operators/interpolate.ts +++ b/packages/vx-scale/src/operators/interpolate.ts @@ -12,7 +12,11 @@ export default function applyInterpolate< scale: D3Scale, config: ScaleConfigWithoutType, ) { - if ('interpolate' in config && config.interpolate && 'interpolate' in scale) { + if ( + 'interpolate' in config && + 'interpolate' in scale && + typeof config.interpolate !== 'undefined' + ) { const interpolator = createColorInterpolator(config.interpolate); scale.interpolate((interpolator as unknown) as InterpolatorFactory); } diff --git a/packages/vx-scale/src/operators/nice.ts b/packages/vx-scale/src/operators/nice.ts index 1dc3c41f9..8bd81c788 100644 --- a/packages/vx-scale/src/operators/nice.ts +++ b/packages/vx-scale/src/operators/nice.ts @@ -73,7 +73,7 @@ export default function applyNice< ? utcIntervals[interval] : localTimeIntervals[interval] ).every(step); - if (parsedInterval !== null) { + if (parsedInterval != null) { timeScale.nice(parsedInterval as CountableTimeInterval); } } From dc8786f0d8d4f016fd5b437f8fc70059ee77e95b Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 13:50:14 -0700 Subject: [PATCH 29/42] fix: type --- packages/vx-scale/src/updateScale.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index 37419b9bf..e5432520a 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -1,4 +1,4 @@ -import { ScaleConfig, PickScaleConfigWithoutType } from './types/ScaleConfig'; +import { PickScaleConfigWithoutType, ScaleConfigWithoutType } from './types/ScaleConfig'; import { DefaultThresholdInput, D3Scale, PickD3Scale } from './types/Scale'; import { StringLike, DefaultOutput } from './types/Base'; import { updateThresholdScale } from './scales/threshold'; @@ -155,7 +155,7 @@ function updateScale< ThresholdInput extends DefaultThresholdInput >( scale: D3Scale, - config?: Omit, 'type'>, + config?: ScaleConfigWithoutType, ) { const scaleOut = scale.copy() as D3Scale; From 653693d4407c68c1387b169ef3f429a94b033c09 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 14:00:00 -0700 Subject: [PATCH 30/42] fix: enforce order --- .../vx-scale/src/operators/scaleOperator.ts | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/packages/vx-scale/src/operators/scaleOperator.ts b/packages/vx-scale/src/operators/scaleOperator.ts index 7c7ea76fa..bc8d8602c 100644 --- a/packages/vx-scale/src/operators/scaleOperator.ts +++ b/packages/vx-scale/src/operators/scaleOperator.ts @@ -15,17 +15,39 @@ import round from './round'; import unknown from './unknown'; import zero from './zero'; -const operators = { +/** + * List of all operators, in order of execution + */ +export const ALL_OPERATORS = [ // domain => nice => zero + 'domain', + 'nice', + 'zero', + + // interpolate before round + 'interpolate', + 'round', + + // Order does not matter for these operators + 'align', + 'base', + 'clamp', + 'constant', + 'exponent', + 'padding', + 'range', + 'unknown', +] as const; + +type OperatorType = typeof ALL_OPERATORS[number]; + +// Use Record to enforce that all keys in OperatorType must exist. +const operators: Record = { domain, nice, zero, - - // interpolate before round interpolate, round, - - // Order does not matter for these operators align, base, clamp, @@ -36,13 +58,9 @@ const operators = { unknown, }; -type OperatorType = keyof typeof operators; - -const orderedOps = Object.keys(operators) as OperatorType[]; - export default function scaleOperator(...ops: OperatorType[]) { const selection = new Set(ops); - const selectedOps = orderedOps.filter(o => selection.has(o)); + const selectedOps = ALL_OPERATORS.filter(o => selection.has(o)); return function applyOperators< Output = DefaultOutput, From 90a5fee86f5ca3121d0e5902078374453b451fb8 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 15:45:38 -0700 Subject: [PATCH 31/42] test: add unit test for scales --- packages/vx-scale/test/scaleBand.test.ts | 49 ++++++------ packages/vx-scale/test/scaleLinear.test.ts | 12 +++ packages/vx-scale/test/scaleLog.test.ts | 53 ++++++++++++- packages/vx-scale/test/scaleOrdinal.test.ts | 16 +++- packages/vx-scale/test/scalePoint.test.ts | 31 +++++++- packages/vx-scale/test/scalePower.test.ts | 79 +++++++++++++++---- packages/vx-scale/test/scaleQuantile.test.ts | 16 ++-- packages/vx-scale/test/scaleQuantize.test.ts | 40 +++++++--- packages/vx-scale/test/scaleSqrt.test.ts | 76 ++++++++++++++---- packages/vx-scale/test/scaleSymlog.test.ts | 49 ++++++++++-- packages/vx-scale/test/scaleThreshold.test.ts | 16 ++-- packages/vx-scale/test/scaleTime.test.ts | 71 ++++++++++++++++- packages/vx-scale/test/scaleUtc.test.ts | 67 +++++++++++++++- 13 files changed, 470 insertions(+), 105 deletions(-) diff --git a/packages/vx-scale/test/scaleBand.test.ts b/packages/vx-scale/test/scaleBand.test.ts index c28a5cd11..0fcd48bcf 100644 --- a/packages/vx-scale/test/scaleBand.test.ts +++ b/packages/vx-scale/test/scaleBand.test.ts @@ -1,43 +1,42 @@ import { scaleBand } from '../src'; describe('scaleBand', () => { - test('it should be defined', () => { + it('should be defined', () => { expect(scaleBand).toBeDefined(); }); - - test('range param should set scale range', () => { - const range = [2, 3] as [number, number]; - const scale = scaleBand({ range }); - expect(scale.range()).toEqual(range); + it('set domain', () => { + const domain = [0, 350]; + const scale = scaleBand({ domain }); + expect(scale.domain()).toEqual(domain); }); - - describe('round param should set rounding', () => { - test('round = true', () => { + it('set range', () => { + const scale = scaleBand({ range: [2, 3] }); + expect(scale.range()).toEqual([2, 3]); + }); + it('set align', () => { + expect(scaleBand({ align: 0.5 }).align()).toEqual(0.5); + }); + it('set padding', () => { + expect(scaleBand({ padding: 0.3 }).padding()).toEqual(0.3); + }); + it('set paddingInner', () => { + expect(scaleBand({ paddingInner: 0.7 }).paddingInner()).toEqual(0.7); + }); + it('set paddingOuter', () => { + expect(scaleBand({ paddingOuter: 0.7 }).paddingOuter()).toEqual(0.7); + }); + describe('set round', () => { + it('true', () => { const scale = scaleBand({ domain: ['a', 'b', 'c'], range: [1.1, 3.5], round: true }); expect(scale('a')).toEqual(2); expect(scale('b')).toEqual(2); expect(scale('c')).toEqual(2); }); - test('round = false', () => { + it('false', () => { const scale = scaleBand({ domain: ['a', 'b', 'c'], range: [1.1, 3.5], round: false }); expect(scale('a')).toEqual(1.1); expect(scale('b')).toEqual(1.9); expect(scale('c')).toEqual(2.7); }); }); - - test('domain param should set scale domain', () => { - const domain = ['a', 'b']; - const scale = scaleBand({ domain }); - expect(scale.domain()).toEqual(domain); - }); - - test('padding param should set scale padding inner & outer', () => { - const padding = 0.75; - const range = [0, 1] as [number, number]; - const domain = ['a', 'b']; - const scale = scaleBand({ padding, range, domain }); - expect(scale.paddingInner()).toEqual(padding); - expect(scale.paddingOuter()).toEqual(padding); - }); }); diff --git a/packages/vx-scale/test/scaleLinear.test.ts b/packages/vx-scale/test/scaleLinear.test.ts index 05d9eeda1..861d8af08 100644 --- a/packages/vx-scale/test/scaleLinear.test.ts +++ b/packages/vx-scale/test/scaleLinear.test.ts @@ -52,4 +52,16 @@ describe('scaleLinear()', () => { expect(scale(2.6)).toEqual(2.6); }); }); + describe('set zero', () => { + it('true', () => { + expect(scaleLinear({ domain: [1, 2], zero: true }).domain()).toEqual([0, 2]); + expect(scaleLinear({ domain: [-2, -1], zero: true }).domain()).toEqual([-2, 0]); + expect(scaleLinear({ domain: [-2, 3], zero: true }).domain()).toEqual([-2, 3]); + }); + it('false', () => { + expect(scaleLinear({ domain: [1, 2], zero: false }).domain()).toEqual([1, 2]); + expect(scaleLinear({ domain: [-2, -1], zero: false }).domain()).toEqual([-2, -1]); + expect(scaleLinear({ domain: [-2, 3], zero: false }).domain()).toEqual([-2, 3]); + }); + }); }); diff --git a/packages/vx-scale/test/scaleLog.test.ts b/packages/vx-scale/test/scaleLog.test.ts index 838e36b76..52a33d18f 100644 --- a/packages/vx-scale/test/scaleLog.test.ts +++ b/packages/vx-scale/test/scaleLog.test.ts @@ -1,7 +1,56 @@ import { scaleLog } from '../src'; -describe('scaleLog', () => { - test('it should be defined', () => { +describe('scaleLog()', () => { + it('should be defined', () => { expect(scaleLog).toBeDefined(); }); + it('set domain', () => { + const domain = [1, 2]; + expect(scaleLog({ domain: [1, 2] }).domain()).toEqual(domain); + }); + it('set range', () => { + const range = [1, 2]; + expect(scaleLog({ range: [1, 2] }).range()).toEqual(range); + }); + it('set base', () => { + expect(scaleLog({ base: 2 }).base()).toEqual(2); + }); + describe('set clamp', () => { + it('true', () => { + const scale = scaleLog({ range: [1, 2], clamp: true }); + expect(scale(100)).toEqual(2); + }); + it('false', () => { + const scale = scaleLog({ range: [1, 2], clamp: false }); + expect(scale(100)).toEqual(3); + }); + }); + it('set (color) interpolate', () => { + const scale = scaleLog({ + domain: [1, 100], + range: ['#ff0000', '#000000'], + interpolate: 'lab', + }); + expect(scale(10)).toEqual('rgb(122, 27, 11)'); + }); + describe('set nice', () => { + it('true', () => { + const scale = scaleLog({ domain: [0.1, 0.91], nice: true }); + expect(scale.domain()).toEqual([0.1, 1]); + }); + it('false', () => { + const scale = scaleLog({ domain: [0.1, 0.91], nice: false }); + expect(scale.domain()).toEqual([0.1, 0.91]); + }); + }); + describe('set round', () => { + it('true', () => { + const scale = scaleLog({ domain: [1, 10], range: [1, 10], round: true }); + expect(scale(2.2)).toEqual(4); + }); + it('false', () => { + const scale = scaleLog({ domain: [1, 10], range: [1, 10], round: false }); + expect(scale(5).toFixed(2)).toEqual('7.29'); + }); + }); }); diff --git a/packages/vx-scale/test/scaleOrdinal.test.ts b/packages/vx-scale/test/scaleOrdinal.test.ts index 3752a55e7..892906be9 100644 --- a/packages/vx-scale/test/scaleOrdinal.test.ts +++ b/packages/vx-scale/test/scaleOrdinal.test.ts @@ -1,7 +1,21 @@ import { scaleOrdinal } from '../src'; describe('scaleOrdinal', () => { - test('it should be defined', () => { + it('should be defined', () => { expect(scaleOrdinal).toBeDefined(); }); + it('set domain', () => { + const domain = ['noodle', 'burger']; + const scale = scaleOrdinal({ domain }); + expect(scale.domain()).toEqual(domain); + }); + it('set range', () => { + const range = ['red', 'green']; + const scale = scaleOrdinal({ range }); + expect(scale.range()).toEqual(range); + }); + it('set unknown', () => { + const scale = scaleOrdinal({ domain: ['noodle', 'burger'], unknown: 'green' }); + expect(scale('sandwich')).toEqual('green'); + }); }); diff --git a/packages/vx-scale/test/scalePoint.test.ts b/packages/vx-scale/test/scalePoint.test.ts index 8c5580428..3a239a442 100644 --- a/packages/vx-scale/test/scalePoint.test.ts +++ b/packages/vx-scale/test/scalePoint.test.ts @@ -1,7 +1,36 @@ import { scalePoint } from '../src'; describe('scalePoint', () => { - test('it should be defined', () => { + it('should be defined', () => { expect(scalePoint).toBeDefined(); }); + it('set domain', () => { + const domain = [0, 350]; + const scale = scalePoint({ domain }); + expect(scale.domain()).toEqual(domain); + }); + it('set range', () => { + const scale = scalePoint({ range: [2, 3] }); + expect(scale.range()).toEqual([2, 3]); + }); + it('set align', () => { + expect(scalePoint({ align: 0.5 }).align()).toEqual(0.5); + }); + it('set padding', () => { + expect(scalePoint({ padding: 0.5 }).padding()).toEqual(0.5); + }); + describe('set round', () => { + it('true', () => { + const scale = scalePoint({ domain: ['a', 'b', 'c'], range: [1.1, 3.5], round: true }); + expect(scale('a')).toEqual(1); + expect(scale('b')).toEqual(2); + expect(scale('c')).toEqual(3); + }); + it('false', () => { + const scale = scalePoint({ domain: ['a', 'b', 'c'], range: [1.1, 3.5], round: false }); + expect(scale('a')).toEqual(1.1); + expect(scale('b')).toEqual(2.3); + expect(scale('c')).toEqual(3.5); + }); + }); }); diff --git a/packages/vx-scale/test/scalePower.test.ts b/packages/vx-scale/test/scalePower.test.ts index f748d6c98..303c720db 100644 --- a/packages/vx-scale/test/scalePower.test.ts +++ b/packages/vx-scale/test/scalePower.test.ts @@ -1,25 +1,70 @@ import { scalePower } from '../src'; -describe('scalePower', () => { - test('it should be defined', () => { +describe('scalePower()', () => { + it('should be defined', () => { expect(scalePower).toBeDefined(); }); - - test('exponent param should set scale exponent', () => { - const exponent = 2; - const scale = scalePower({ exponent }); - expect(scale.exponent()).toEqual(exponent); + it('set domain', () => { + const domain = [1, 2]; + expect(scalePower({ domain: [1, 2] }).domain()).toEqual(domain); }); - - test('range param should set scale range', () => { - const range = [2, 3]; - const scale = scalePower({ range }); - expect(scale.range()).toEqual(range); + it('set range', () => { + const range = [1, 2]; + expect(scalePower({ range: [1, 2] }).range()).toEqual(range); }); - - test('domain param should set scale domain', () => { - const domain = [0, 350]; - const scale = scalePower({ domain }); - expect(scale.domain()).toEqual(domain); + it('set exponent', () => { + expect(scalePower({ exponent: 3 }).exponent()).toEqual(3); + }); + describe('set clamp', () => { + it('true', () => { + const scale = scalePower({ clamp: true }); + expect(scale(10)).toEqual(1); + }); + it('false', () => { + const scale = scalePower({ clamp: false }); + expect(scale(10)).toEqual(10); + }); + }); + it('set (color) interpolate', () => { + const scale = scalePower({ + domain: [0, 10], + range: ['#ff0000', '#000000'], + interpolate: 'lab', + }); + expect(scale(5)).toEqual('rgb(122, 27, 11)'); + }); + describe('set nice', () => { + it('true', () => { + const scale = scalePower({ domain: [0.1, 0.91], nice: true }); + expect(scale.domain()).toEqual([0.1, 1]); + }); + it('false', () => { + const scale = scalePower({ domain: [0.1, 0.91], nice: false }); + expect(scale.domain()).toEqual([0.1, 0.91]); + }); + }); + describe('set round', () => { + it('true', () => { + const scale = scalePower({ domain: [0, 10], range: [0, 10], round: true }); + expect(scale(2.2)).toEqual(2); + expect(scale(2.6)).toEqual(3); + }); + it('false', () => { + const scale = scalePower({ domain: [0, 10], range: [0, 10], round: false }); + expect(scale(2.2)).toEqual(2.2); + expect(scale(2.6)).toEqual(2.6); + }); + }); + describe('set zero', () => { + it('true', () => { + expect(scalePower({ domain: [1, 2], zero: true }).domain()).toEqual([0, 2]); + expect(scalePower({ domain: [-2, -1], zero: true }).domain()).toEqual([-2, 0]); + expect(scalePower({ domain: [-2, 3], zero: true }).domain()).toEqual([-2, 3]); + }); + it('false', () => { + expect(scalePower({ domain: [1, 2], zero: false }).domain()).toEqual([1, 2]); + expect(scalePower({ domain: [-2, -1], zero: false }).domain()).toEqual([-2, -1]); + expect(scalePower({ domain: [-2, 3], zero: false }).domain()).toEqual([-2, 3]); + }); }); }); diff --git a/packages/vx-scale/test/scaleQuantile.test.ts b/packages/vx-scale/test/scaleQuantile.test.ts index e518fd510..ac943567d 100644 --- a/packages/vx-scale/test/scaleQuantile.test.ts +++ b/packages/vx-scale/test/scaleQuantile.test.ts @@ -1,19 +1,17 @@ import { scaleQuantile } from '../src'; describe('scaleQuantile', () => { - test('it should be defined', () => { + it('should be defined', () => { expect(scaleQuantile).toBeDefined(); }); - - test('range param should set scale range', () => { - const range = [2, 3]; - const scale = scaleQuantile({ range }); - expect(scale.range()).toEqual(range); - }); - - test('domain param should set scale domain', () => { + it('set domain', () => { const domain = [0, 350]; const scale = scaleQuantile({ domain }); expect(scale.domain()).toEqual(domain); }); + it('set range', () => { + const range = [2, 3]; + const scale = scaleQuantile({ range }); + expect(scale.range()).toEqual(range); + }); }); diff --git a/packages/vx-scale/test/scaleQuantize.test.ts b/packages/vx-scale/test/scaleQuantize.test.ts index aca992c7e..826c6bf91 100644 --- a/packages/vx-scale/test/scaleQuantize.test.ts +++ b/packages/vx-scale/test/scaleQuantize.test.ts @@ -1,19 +1,37 @@ import { scaleQuantize } from '../src'; describe('scaleQuantize', () => { - test('it should be defined', () => { + it('should be defined', () => { expect(scaleQuantize).toBeDefined(); }); - - test('range param should set scale range', () => { - const range = [2, 3]; - const scale = scaleQuantize({ range }); - expect(scale.range()).toEqual(range); + it('set domain', () => { + const domain = [1, 2]; + expect(scaleQuantize({ domain: [1, 2] }).domain()).toEqual(domain); }); - - test('domain param should set scale domain', () => { - const domain = [0, 350] as [number, number]; - const scale = scaleQuantize({ domain }); - expect(scale.domain()).toEqual(domain); + it('set range', () => { + const range = [1, 2]; + expect(scaleQuantize({ range: [1, 2] }).range()).toEqual(range); + }); + describe('set nice', () => { + it('true', () => { + const scale = scaleQuantize({ domain: [0.1, 0.91], nice: true }); + expect(scale.domain()).toEqual([0.1, 1]); + }); + it('false', () => { + const scale = scaleQuantize({ domain: [0.1, 0.91], nice: false }); + expect(scale.domain()).toEqual([0.1, 0.91]); + }); + }); + describe('set zero', () => { + it('true', () => { + expect(scaleQuantize({ domain: [1, 2], zero: true }).domain()).toEqual([0, 2]); + expect(scaleQuantize({ domain: [-2, -1], zero: true }).domain()).toEqual([-2, 0]); + expect(scaleQuantize({ domain: [-2, 3], zero: true }).domain()).toEqual([-2, 3]); + }); + it('false', () => { + expect(scaleQuantize({ domain: [1, 2], zero: false }).domain()).toEqual([1, 2]); + expect(scaleQuantize({ domain: [-2, -1], zero: false }).domain()).toEqual([-2, -1]); + expect(scaleQuantize({ domain: [-2, 3], zero: false }).domain()).toEqual([-2, 3]); + }); }); }); diff --git a/packages/vx-scale/test/scaleSqrt.test.ts b/packages/vx-scale/test/scaleSqrt.test.ts index 9c1b0491a..3cdf88c76 100644 --- a/packages/vx-scale/test/scaleSqrt.test.ts +++ b/packages/vx-scale/test/scaleSqrt.test.ts @@ -1,24 +1,68 @@ import { scaleSqrt } from '../src'; -describe('scaleSqrt', () => { - test('it should be defined', () => { +describe('scaleSqrt()', () => { + it('should be defined', () => { expect(scaleSqrt).toBeDefined(); }); - - test('exponent param should be 0.5', () => { - const scale = scaleSqrt({}); - expect(scale.exponent()).toEqual(0.5); + it('set domain', () => { + const domain = [1, 2]; + expect(scaleSqrt({ domain: [1, 2] }).domain()).toEqual(domain); }); - - test('range param should set scale range', () => { - const range = [2, 3]; - const scale = scaleSqrt({ range }); - expect(scale.range()).toEqual(range); + it('set range', () => { + const range = [1, 2]; + expect(scaleSqrt({ range: [1, 2] }).range()).toEqual(range); }); - - test('domain param should set scasle domain', () => { - const domain = [0, 350]; - const scale = scaleSqrt({ domain }); - expect(scale.domain()).toEqual(domain); + it('exponent is 0.5', () => { + expect(scaleSqrt({}).exponent()).toEqual(0.5); + }); + describe('set clamp', () => { + it('true', () => { + const scale = scaleSqrt({ clamp: true }); + expect(scale(10)).toEqual(1); + }); + it('false', () => { + const scale = scaleSqrt({ clamp: false }); + expect(scale(10).toFixed(2)).toEqual('3.16'); + }); + }); + it('set (color) interpolate', () => { + const scale = scaleSqrt({ + domain: [0, 10], + range: ['#ff0000', '#000000'], + interpolate: 'lab', + }); + expect(scale(5)).toEqual('rgb(73, 23, 9)'); + }); + describe('set nice', () => { + it('true', () => { + const scale = scaleSqrt({ domain: [0.1, 0.91], nice: true }); + expect(scale.domain()).toEqual([0.1, 1]); + }); + it('false', () => { + const scale = scaleSqrt({ domain: [0.1, 0.91], nice: false }); + expect(scale.domain()).toEqual([0.1, 0.91]); + }); + }); + describe('set round', () => { + it('true', () => { + const scale = scaleSqrt({ domain: [0, 4], range: [0, 2], round: true }); + expect(scale(3)).toEqual(2); + }); + it('false', () => { + const scale = scaleSqrt({ domain: [0, 4], range: [0, 2], round: false }); + expect(scale(3).toFixed(2)).toEqual('1.73'); + }); + }); + describe('set zero', () => { + it('true', () => { + expect(scaleSqrt({ domain: [1, 2], zero: true }).domain()).toEqual([0, 2]); + expect(scaleSqrt({ domain: [-2, -1], zero: true }).domain()).toEqual([-2, 0]); + expect(scaleSqrt({ domain: [-2, 3], zero: true }).domain()).toEqual([-2, 3]); + }); + it('false', () => { + expect(scaleSqrt({ domain: [1, 2], zero: false }).domain()).toEqual([1, 2]); + expect(scaleSqrt({ domain: [-2, -1], zero: false }).domain()).toEqual([-2, -1]); + expect(scaleSqrt({ domain: [-2, 3], zero: false }).domain()).toEqual([-2, 3]); + }); }); }); diff --git a/packages/vx-scale/test/scaleSymlog.test.ts b/packages/vx-scale/test/scaleSymlog.test.ts index a2f176a65..017f2dbb6 100644 --- a/packages/vx-scale/test/scaleSymlog.test.ts +++ b/packages/vx-scale/test/scaleSymlog.test.ts @@ -1,19 +1,52 @@ import { scaleSymlog } from '../src'; describe('scaleSymlog', () => { - test('it should be defined', () => { + it('should be defined', () => { expect(scaleSymlog).toBeDefined(); }); - - test('range param should set scale range', () => { - const range = [2, 3]; - const scale = scaleSymlog({ range }); - expect(scale.range()).toEqual(range); + it('set domain', () => { + const domain = [1, 2]; + expect(scaleSymlog({ domain: [1, 2] }).domain()).toEqual(domain); }); - - test('constant param should set scale constant', () => { + it('set range', () => { + const range = [1, 2]; + expect(scaleSymlog({ range: [1, 2] }).range()).toEqual(range); + }); + describe('set clamp', () => { + it('true', () => { + const scale = scaleSymlog({ clamp: true }); + expect(scale(10)).toEqual(1); + }); + it('false', () => { + const scale = scaleSymlog({ clamp: false }); + expect(scale(10).toFixed(2)).toEqual('3.46'); + }); + }); + it('set constant', () => { const constant = 2; const scale = scaleSymlog({ constant }); expect(scale.constant()).toEqual(constant); }); + describe('set nice', () => { + it('true', () => { + const scale = scaleSymlog({ domain: [0.1, 0.91], nice: true }); + expect(scale.domain()).toEqual([0.1, 1]); + }); + it('false', () => { + const scale = scaleSymlog({ domain: [0.1, 0.91], nice: false }); + expect(scale.domain()).toEqual([0.1, 0.91]); + }); + }); + describe('set zero', () => { + it('true', () => { + expect(scaleSymlog({ domain: [1, 2], zero: true }).domain()).toEqual([0, 2]); + expect(scaleSymlog({ domain: [-2, -1], zero: true }).domain()).toEqual([-2, 0]); + expect(scaleSymlog({ domain: [-2, 3], zero: true }).domain()).toEqual([-2, 3]); + }); + it('false', () => { + expect(scaleSymlog({ domain: [1, 2], zero: false }).domain()).toEqual([1, 2]); + expect(scaleSymlog({ domain: [-2, -1], zero: false }).domain()).toEqual([-2, -1]); + expect(scaleSymlog({ domain: [-2, 3], zero: false }).domain()).toEqual([-2, 3]); + }); + }); }); diff --git a/packages/vx-scale/test/scaleThreshold.test.ts b/packages/vx-scale/test/scaleThreshold.test.ts index 19783ff90..44c2c70b0 100644 --- a/packages/vx-scale/test/scaleThreshold.test.ts +++ b/packages/vx-scale/test/scaleThreshold.test.ts @@ -1,19 +1,17 @@ import { scaleThreshold } from '../src'; describe('scaleThreshold', () => { - test('it should be defined', () => { + it('should be defined', () => { expect(scaleThreshold).toBeDefined(); }); - - test('range param should set scale range', () => { - const range = [2, 3]; - const scale = scaleThreshold({ range }); - expect(scale.range()).toEqual(range); - }); - - test('domain param should set scale domain', () => { + it('set domain', () => { const domain = [0, 350]; const scale = scaleThreshold({ domain }); expect(scale.domain()).toEqual(domain); }); + it('set range', () => { + const range = [2, 3]; + const scale = scaleThreshold({ range }); + expect(scale.range()).toEqual(range); + }); }); diff --git a/packages/vx-scale/test/scaleTime.test.ts b/packages/vx-scale/test/scaleTime.test.ts index 209cf23cb..a0887c904 100644 --- a/packages/vx-scale/test/scaleTime.test.ts +++ b/packages/vx-scale/test/scaleTime.test.ts @@ -1,7 +1,74 @@ import { scaleTime } from '../src'; -describe('scaleTime', () => { - test('it should be defined', () => { +describe('scaleTime()', () => { + const domain = [new Date(2020, 0, 1), new Date(2020, 0, 10)]; + + it('should be defined', () => { expect(scaleTime).toBeDefined(); }); + it('set domain', () => { + expect(scaleTime({ domain }).domain()).toEqual(domain); + }); + it('set range', () => { + const range = [1, 2]; + expect(scaleTime({ range: [1, 2] }).range()).toEqual(range); + }); + describe('set clamp', () => { + it('true', () => { + const scale = scaleTime({ domain, range: [0, 10], clamp: true }); + expect(scale(new Date(2019, 11, 31))).toEqual(0); + }); + it('false', () => { + const scale = scaleTime({ domain, range: [0, 10], clamp: false }); + expect(scale(new Date(2019, 11, 31)).toFixed(2)).toEqual('-1.11'); + }); + }); + it('set (color) interpolate', () => { + const scale = scaleTime({ + domain, + range: ['#ff0000', '#000000'], + interpolate: 'lab', + }); + expect(scale(new Date(2020, 0, 5))).toEqual('rgb(136, 28, 11)'); + }); + describe('set nice', () => { + const unniceDomain = [new Date(2020, 0, 1), new Date(2020, 0, 9, 20)]; + it('true', () => { + const scale = scaleTime({ + domain: unniceDomain, + nice: true, + }); + expect(scale.domain()).toEqual(domain); + }); + it('false', () => { + const scale = scaleTime({ domain: unniceDomain, nice: false }); + expect(scale.domain()).toEqual(unniceDomain); + }); + it('nice string', () => { + const scale = scaleTime({ domain: unniceDomain, nice: 'hour' }); + expect(scale.domain()).toEqual(unniceDomain); + }); + it('nice object', () => { + const scale = scaleTime({ domain: unniceDomain, nice: { interval: 'hour', step: 3 } }); + expect(scale.domain()).toEqual([new Date(2020, 0, 1), new Date(2020, 0, 9, 21)]); + }); + }); + describe('set round', () => { + it('true', () => { + const scale = scaleTime({ + domain, + range: [1, 5], + round: true, + }); + expect(scale(new Date(2020, 0, 5))).toEqual(3); + }); + it('false', () => { + const scale = scaleTime({ + domain, + range: [1, 5], + round: false, + }); + expect(scale(new Date(2020, 0, 5)).toFixed(2)).toEqual('2.78'); + }); + }); }); diff --git a/packages/vx-scale/test/scaleUtc.test.ts b/packages/vx-scale/test/scaleUtc.test.ts index bc91fc620..ccb879bb1 100644 --- a/packages/vx-scale/test/scaleUtc.test.ts +++ b/packages/vx-scale/test/scaleUtc.test.ts @@ -1,7 +1,66 @@ -import { scaleUtc } from '../src'; +import { scaleTime } from '../src'; -describe('scaleUtc', () => { - test('it should be defined', () => { - expect(scaleUtc).toBeDefined(); +describe('scaleTime()', () => { + const domain = [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 10))]; + + it('should be defined', () => { + expect(scaleTime).toBeDefined(); + }); + it('set domain', () => { + expect(scaleTime({ domain }).domain()).toEqual(domain); + }); + it('set range', () => { + const range = [1, 2]; + expect(scaleTime({ range: [1, 2] }).range()).toEqual(range); + }); + describe('set clamp', () => { + it('true', () => { + const scale = scaleTime({ domain, range: [0, 10], clamp: true }); + expect(scale(new Date(Date.UTC(2019, 11, 31)))).toEqual(0); + }); + it('false', () => { + const scale = scaleTime({ domain, range: [0, 10], clamp: false }); + expect(scale(new Date(Date.UTC(2019, 11, 31))).toFixed(2)).toEqual('-1.11'); + }); + }); + it('set (color) interpolate', () => { + const scale = scaleTime({ + domain, + range: ['#ff0000', '#000000'], + interpolate: 'lab', + }); + expect(scale(new Date(Date.UTC(2020, 0, 5)))).toEqual('rgb(136, 28, 11)'); + }); + describe('set nice', () => { + const unniceDomain = [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 9, 20))]; + it('true', () => { + const scale = scaleTime({ + domain: unniceDomain, + nice: true, + }); + expect(scale.domain()).toEqual(domain); + }); + it('false', () => { + const scale = scaleTime({ domain: unniceDomain, nice: false }); + expect(scale.domain()).toEqual(unniceDomain); + }); + }); + describe('set round', () => { + it('true', () => { + const scale = scaleTime({ + domain, + range: [1, 5], + round: true, + }); + expect(scale(new Date(Date.UTC(2020, 0, 5)))).toEqual(3); + }); + it('false', () => { + const scale = scaleTime({ + domain, + range: [1, 5], + round: false, + }); + expect(scale(new Date(Date.UTC(2020, 0, 5))).toFixed(2)).toEqual('2.78'); + }); }); }); From eff70911a1c1beaa7fa2fd899dcb91e5a2866f4c Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 16:10:27 -0700 Subject: [PATCH 32/42] test: add createScale test --- packages/vx-scale/test/createScale.test.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/vx-scale/test/createScale.test.ts diff --git a/packages/vx-scale/test/createScale.test.ts b/packages/vx-scale/test/createScale.test.ts new file mode 100644 index 000000000..e86824cf9 --- /dev/null +++ b/packages/vx-scale/test/createScale.test.ts @@ -0,0 +1,16 @@ +import { createScale } from '../src'; + +describe('createScale()', () => { + it('linear', () => { + const scale = createScale({ type: 'linear', domain: [0, 10], range: [2, 4] }); + expect(scale(5)).toEqual(3); + }); + it('log', () => { + const scale = createScale({ type: 'log', base: 2, domain: [2, 8], range: [1, 3] }); + expect(scale(4)?.toFixed(2)).toEqual('2.00'); + }); + it('pow', () => { + const scale = createScale({ type: 'pow', exponent: 2, domain: [1, 3], range: [2, 18] }); + expect(scale(2)).toEqual('2.00'); + }); +}); From 820c7068a9de04ddd069bef2beff6a728d5deedf Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 16:37:22 -0700 Subject: [PATCH 33/42] test: more tests --- packages/vx-scale/src/createScale.ts | 89 +++++++++++++++++++--- packages/vx-scale/test/createScale.test.ts | 87 ++++++++++++++++++++- packages/vx-scale/test/updateScale.test.ts | 6 +- 3 files changed, 166 insertions(+), 16 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 4db7207a4..ba651b89a 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -1,4 +1,4 @@ -import { ScaleConfig, ScaleConfigToD3Scale, PickScaleConfigWithoutType } from './types/ScaleConfig'; +import { ScaleConfig, PickScaleConfigWithoutType, PickScaleConfig } from './types/ScaleConfig'; import { DefaultThresholdInput, PickD3Scale } from './types/Scale'; import { StringLike, DefaultOutput } from './types/Base'; import createLinearScale from './scales/linear'; @@ -22,21 +22,90 @@ import createBandScale from './scales/band'; function createScale< Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, - ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, - Config extends ScaleConfig = ScaleConfig< - Output, - DiscreteInput, - ThresholdInput - > ->(config: Config): ScaleConfigToD3Scale; + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( + config: PickScaleConfig<'linear', Output> | PickScaleConfigWithoutType<'linear', Output>, +): PickD3Scale<'linear', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>(config: PickScaleConfig<'log', Output>): PickD3Scale<'log', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>(config: PickScaleConfig<'pow', Output>): PickD3Scale<'pow', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>(config: PickScaleConfig<'sqrt', Output>): PickD3Scale<'sqrt', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>(config: PickScaleConfig<'symlog', Output>): PickD3Scale<'symlog', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>(config: PickScaleConfig<'time', Output>): PickD3Scale<'time', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>(config: PickScaleConfig<'utc', Output>): PickD3Scale<'utc', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>(config: PickScaleConfig<'quantile', Output>): PickD3Scale<'quantile', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>(config: PickScaleConfig<'quantize', Output>): PickD3Scale<'quantize', Output>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( + config: PickScaleConfig<'threshold', Output, StringLike, ThresholdInput>, +): PickD3Scale<'threshold', Output, StringLike, ThresholdInput>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( + config: PickScaleConfig<'ordinal', Output, DiscreteInput>, +): PickD3Scale<'ordinal', Output, DiscreteInput>; + +function createScale< + Output = DefaultOutput, + DiscreteInput extends StringLike = StringLike, + ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput +>( + config: PickScaleConfig<'point', Output, DiscreteInput>, +): PickD3Scale<'point', Output, DiscreteInput>; function createScale< Output = DefaultOutput, DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( - config: PickScaleConfigWithoutType<'linear', Output>, -): PickD3Scale<'linear', Output, DiscreteInput, ThresholdInput>; + config: PickScaleConfig<'band', Output, DiscreteInput>, +): PickD3Scale<'band', Output, DiscreteInput>; // Actual implementation diff --git a/packages/vx-scale/test/createScale.test.ts b/packages/vx-scale/test/createScale.test.ts index e86824cf9..aa26682ec 100644 --- a/packages/vx-scale/test/createScale.test.ts +++ b/packages/vx-scale/test/createScale.test.ts @@ -5,12 +5,93 @@ describe('createScale()', () => { const scale = createScale({ type: 'linear', domain: [0, 10], range: [2, 4] }); expect(scale(5)).toEqual(3); }); + it('fallbacks to linear if type is not defined', () => { + const scale = createScale({ domain: [0, 10], range: [2, 4] }); + expect(scale(5)).toEqual(3); + }); it('log', () => { - const scale = createScale({ type: 'log', base: 2, domain: [2, 8], range: [1, 3] }); - expect(scale(4)?.toFixed(2)).toEqual('2.00'); + const scale = createScale({ + type: 'log', + base: 2, + domain: [2, 8], + range: [1, 3], + }); + expect(scale(4).toFixed(2)).toEqual('2.00'); }); it('pow', () => { const scale = createScale({ type: 'pow', exponent: 2, domain: [1, 3], range: [2, 18] }); - expect(scale(2)).toEqual('2.00'); + expect(scale(2)).toEqual(8); + }); + it('sqrt', () => { + const scale = createScale({ type: 'sqrt', domain: [1, 9], range: [1, 3] }); + expect(scale(4)).toEqual(2); + }); + it('symlog', () => { + const scale = createScale({ type: 'symlog', domain: [1, 9], range: [1, 3], constant: 2 }); + expect(scale(4).toFixed(2)).toEqual('2.07'); + }); + it('time', () => { + const scale = createScale({ + type: 'time', + domain: [new Date(2020, 0, 1), new Date(2020, 0, 10)], + range: [1, 10], + }); + expect(scale(new Date(2020, 0, 4))).toEqual(4); + }); + it('utc', () => { + const scale = createScale({ + type: 'time', + domain: [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 10))], + range: [1, 10], + }); + expect(scale(new Date(Date.UTC(2020, 0, 4)))).toEqual(4); + }); + it('quantile', () => { + const scale = createScale({ type: 'quantile', domain: [1, 3, 5, 7], range: [0, 10] }); + expect(scale(2)).toEqual(0); + }); + it('quantize', () => { + const scale = createScale({ type: 'quantize', domain: [1, 10], range: ['red', 'green'] }); + expect(scale(2)).toEqual('red'); + expect(scale(6)).toEqual('green'); + }); + it('threshold', () => { + const scale = createScale({ + type: 'quantize', + domain: [0, 1], + range: ['red', 'white', 'green'], + }); + expect(scale(-1)).toEqual('red'); + expect(scale(0)).toEqual('red'); + expect(scale(0.5)).toEqual('white'); + expect(scale(1)).toEqual('green'); + expect(scale(1000)).toEqual('green'); + }); + it('ordinal', () => { + const scale = createScale({ type: 'ordinal', domain: ['pig', 'cat'], range: ['red', 'green'] }); + expect(scale('pig')).toEqual('red'); + expect(scale('cat')).toEqual('green'); + }); + it('point', () => { + const scale = createScale({ + type: 'point', + domain: ['a', 'b', 'c'], + range: [1.1, 3.5], + round: true, + }); + expect(scale('a')).toEqual(1); + expect(scale('b')).toEqual(2); + expect(scale('c')).toEqual(3); + }); + it('band', () => { + const scale = createScale({ + type: 'band', + domain: ['a', 'b', 'c'], + range: [1.1, 3.5], + round: false, + }); + expect(scale('a')).toEqual(1.1); + expect(scale('b')).toEqual(1.9); + expect(scale('c')).toEqual(2.7); }); }); diff --git a/packages/vx-scale/test/updateScale.test.ts b/packages/vx-scale/test/updateScale.test.ts index 6745e9619..9d1b2bc74 100644 --- a/packages/vx-scale/test/updateScale.test.ts +++ b/packages/vx-scale/test/updateScale.test.ts @@ -1,11 +1,11 @@ import { updateScale, scaleLinear } from '../src'; describe('updateScale', () => { - test('it should be defined', () => { + it('should be defined', () => { expect(updateScale).toBeDefined(); }); - test('it should return a new copy of the scale', () => { + it('should return a new copy of the scale', () => { const domain = [0, 350]; const range = [0, 2]; const scale = scaleLinear({ range, domain }); @@ -13,7 +13,7 @@ describe('updateScale', () => { expect(scale).not.toBe(nextScale); }); - test('it should update the new copy of the scale', () => { + it('should update the new copy of the scale', () => { const domain = [0, 350]; const newDomain = [200, 300]; const range = [0, 2]; From 2c7152e705f267b54a981f62ec10e77b684714d2 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 17:09:30 -0700 Subject: [PATCH 34/42] test: more tests --- packages/vx-scale/src/operators/domain.ts | 10 ++-- packages/vx-scale/test/createScale.test.ts | 8 +-- packages/vx-scale/test/scaleLinear.test.ts | 35 ++++++++++--- packages/vx-scale/test/scaleTime.test.ts | 21 ++++++-- packages/vx-scale/test/scaleUtc.test.ts | 57 ++++++++++++++++------ 5 files changed, 99 insertions(+), 32 deletions(-) diff --git a/packages/vx-scale/src/operators/domain.ts b/packages/vx-scale/src/operators/domain.ts index 1213a58e3..78d5a6b47 100644 --- a/packages/vx-scale/src/operators/domain.ts +++ b/packages/vx-scale/src/operators/domain.ts @@ -11,14 +11,14 @@ export default function applyDomain< config: ScaleConfigWithoutType, ) { if (config.domain) { - if ('padding' in scale || 'unknown' in scale) { - // ordinal, point, band scales - scale.domain(config.domain as DiscreteInput[]); - } else if ('nice' in scale) { + if ('nice' in scale || 'quantiles' in scale) { // continuous input scales scale.domain(config.domain as number[] | Date[]); + } else if ('padding' in scale) { + // point and band scales + scale.domain(config.domain as DiscreteInput[]); } else { - // threshold scale + // ordinal and threshold scale scale.domain(config.domain as ThresholdInput[]); } } diff --git a/packages/vx-scale/test/createScale.test.ts b/packages/vx-scale/test/createScale.test.ts index aa26682ec..bd1295978 100644 --- a/packages/vx-scale/test/createScale.test.ts +++ b/packages/vx-scale/test/createScale.test.ts @@ -40,7 +40,7 @@ describe('createScale()', () => { }); it('utc', () => { const scale = createScale({ - type: 'time', + type: 'utc', domain: [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 10))], range: [1, 10], }); @@ -57,12 +57,12 @@ describe('createScale()', () => { }); it('threshold', () => { const scale = createScale({ - type: 'quantize', - domain: [0, 1], + type: 'threshold', + domain: [0, 1] as number[], range: ['red', 'white', 'green'], }); expect(scale(-1)).toEqual('red'); - expect(scale(0)).toEqual('red'); + expect(scale(0)).toEqual('white'); expect(scale(0.5)).toEqual('white'); expect(scale(1)).toEqual('green'); expect(scale(1000)).toEqual('green'); diff --git a/packages/vx-scale/test/scaleLinear.test.ts b/packages/vx-scale/test/scaleLinear.test.ts index 861d8af08..b6f55c570 100644 --- a/packages/vx-scale/test/scaleLinear.test.ts +++ b/packages/vx-scale/test/scaleLinear.test.ts @@ -22,13 +22,36 @@ describe('scaleLinear()', () => { expect(scale(10)).toEqual(10); }); }); - it('set (color) interpolate', () => { - const scale = scaleLinear({ - domain: [0, 10], - range: ['#ff0000', '#000000'], - interpolate: 'lab', + describe('set (color) interpolate', () => { + it('string', () => { + const scale = scaleLinear({ + domain: [0, 10], + range: ['#ff0000', '#000000'], + interpolate: 'lab', + }); + expect(scale(5)).toEqual('rgb(122, 27, 11)'); + }); + it('config object', () => { + const scale = scaleLinear({ + domain: [0, 10], + range: ['#ff0000', '#000000'], + interpolate: { + type: 'rgb', + }, + }); + expect(scale(5)).toEqual('rgb(128, 0, 0)'); + }); + it('config object with gamma', () => { + const scale = scaleLinear({ + domain: [0, 10], + range: ['#ff0000', '#000000'], + interpolate: { + type: 'rgb', + gamma: 0.9, + }, + }); + expect(scale(5)).toEqual('rgb(118, 0, 0)'); }); - expect(scale(5)).toEqual('rgb(122, 27, 11)'); }); describe('set nice', () => { it('true', () => { diff --git a/packages/vx-scale/test/scaleTime.test.ts b/packages/vx-scale/test/scaleTime.test.ts index a0887c904..b6069d842 100644 --- a/packages/vx-scale/test/scaleTime.test.ts +++ b/packages/vx-scale/test/scaleTime.test.ts @@ -1,7 +1,19 @@ +import TimezoneMock from 'timezone-mock'; import { scaleTime } from '../src'; describe('scaleTime()', () => { - const domain = [new Date(2020, 0, 1), new Date(2020, 0, 10)]; + let domain: [Date, Date]; + let unniceDomain: [Date, Date]; + + beforeAll(() => { + TimezoneMock.register('US/Pacific'); + domain = [new Date(2020, 0, 1), new Date(2020, 0, 10)]; + unniceDomain = [new Date(2020, 0, 1), new Date(2020, 0, 9, 20)]; + }); + + afterAll(() => { + TimezoneMock.unregister(); + }); it('should be defined', () => { expect(scaleTime).toBeDefined(); @@ -32,7 +44,6 @@ describe('scaleTime()', () => { expect(scale(new Date(2020, 0, 5))).toEqual('rgb(136, 28, 11)'); }); describe('set nice', () => { - const unniceDomain = [new Date(2020, 0, 1), new Date(2020, 0, 9, 20)]; it('true', () => { const scale = scaleTime({ domain: unniceDomain, @@ -44,7 +55,11 @@ describe('scaleTime()', () => { const scale = scaleTime({ domain: unniceDomain, nice: false }); expect(scale.domain()).toEqual(unniceDomain); }); - it('nice string', () => { + it('number', () => { + const scale = scaleTime({ domain: unniceDomain, nice: 5 }); + expect(scale.domain()).toEqual([new Date(2020, 0, 1), new Date(2020, 0, 11)]); + }); + it('time unit string', () => { const scale = scaleTime({ domain: unniceDomain, nice: 'hour' }); expect(scale.domain()).toEqual(unniceDomain); }); diff --git a/packages/vx-scale/test/scaleUtc.test.ts b/packages/vx-scale/test/scaleUtc.test.ts index ccb879bb1..c3062579a 100644 --- a/packages/vx-scale/test/scaleUtc.test.ts +++ b/packages/vx-scale/test/scaleUtc.test.ts @@ -1,30 +1,42 @@ -import { scaleTime } from '../src'; +import TimezoneMock from 'timezone-mock'; +import { scaleUtc } from '../src'; -describe('scaleTime()', () => { - const domain = [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 10))]; +describe('scaleUtc()', () => { + let domain: [Date, Date]; + let unniceDomain: [Date, Date]; + + beforeAll(() => { + TimezoneMock.register('US/Pacific'); + domain = [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 10))]; + unniceDomain = [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 9, 20))]; + }); + + afterAll(() => { + TimezoneMock.unregister(); + }); it('should be defined', () => { - expect(scaleTime).toBeDefined(); + expect(scaleUtc).toBeDefined(); }); it('set domain', () => { - expect(scaleTime({ domain }).domain()).toEqual(domain); + expect(scaleUtc({ domain }).domain()).toEqual(domain); }); it('set range', () => { const range = [1, 2]; - expect(scaleTime({ range: [1, 2] }).range()).toEqual(range); + expect(scaleUtc({ range: [1, 2] }).range()).toEqual(range); }); describe('set clamp', () => { it('true', () => { - const scale = scaleTime({ domain, range: [0, 10], clamp: true }); + const scale = scaleUtc({ domain, range: [0, 10], clamp: true }); expect(scale(new Date(Date.UTC(2019, 11, 31)))).toEqual(0); }); it('false', () => { - const scale = scaleTime({ domain, range: [0, 10], clamp: false }); + const scale = scaleUtc({ domain, range: [0, 10], clamp: false }); expect(scale(new Date(Date.UTC(2019, 11, 31))).toFixed(2)).toEqual('-1.11'); }); }); it('set (color) interpolate', () => { - const scale = scaleTime({ + const scale = scaleUtc({ domain, range: ['#ff0000', '#000000'], interpolate: 'lab', @@ -32,22 +44,39 @@ describe('scaleTime()', () => { expect(scale(new Date(Date.UTC(2020, 0, 5)))).toEqual('rgb(136, 28, 11)'); }); describe('set nice', () => { - const unniceDomain = [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 9, 20))]; it('true', () => { - const scale = scaleTime({ + const scale = scaleUtc({ domain: unniceDomain, nice: true, }); expect(scale.domain()).toEqual(domain); }); it('false', () => { - const scale = scaleTime({ domain: unniceDomain, nice: false }); + const scale = scaleUtc({ domain: unniceDomain, nice: false }); expect(scale.domain()).toEqual(unniceDomain); }); + it('number', () => { + const scale = scaleUtc({ domain: unniceDomain, nice: 5 }); + expect(scale.domain()).toEqual([ + new Date(Date.UTC(2020, 0, 1)), + new Date(Date.UTC(2020, 0, 11)), + ]); + }); + it('time unit string', () => { + const scale = scaleUtc({ domain: unniceDomain, nice: 'hour' }); + expect(scale.domain()).toEqual(unniceDomain); + }); + it('nice object', () => { + const scale = scaleUtc({ domain: unniceDomain, nice: { interval: 'hour', step: 3 } }); + expect(scale.domain()).toEqual([ + new Date(Date.UTC(2020, 0, 1)), + new Date(Date.UTC(2020, 0, 9, 21)), + ]); + }); }); describe('set round', () => { it('true', () => { - const scale = scaleTime({ + const scale = scaleUtc({ domain, range: [1, 5], round: true, @@ -55,7 +84,7 @@ describe('scaleTime()', () => { expect(scale(new Date(Date.UTC(2020, 0, 5)))).toEqual(3); }); it('false', () => { - const scale = scaleTime({ + const scale = scaleUtc({ domain, range: [1, 5], round: false, From d9d21f2e2f31fb854bb8b80fbc38e040ef67d084 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 18:00:31 -0700 Subject: [PATCH 35/42] fix: make config optional for factories --- packages/vx-scale/src/createScale.ts | 4 +- .../vx-scale/src/operators/scaleOperator.ts | 10 +- packages/vx-scale/src/scales/band.ts | 2 +- packages/vx-scale/src/scales/linear.ts | 2 +- packages/vx-scale/src/scales/log.ts | 2 +- packages/vx-scale/src/scales/ordinal.ts | 2 +- packages/vx-scale/src/scales/point.ts | 2 +- packages/vx-scale/src/scales/power.ts | 2 +- packages/vx-scale/src/scales/quantile.ts | 2 +- packages/vx-scale/src/scales/quantize.ts | 2 +- packages/vx-scale/src/scales/squareRoot.ts | 2 +- packages/vx-scale/src/scales/symlog.ts | 2 +- packages/vx-scale/src/scales/threshold.ts | 2 +- packages/vx-scale/src/scales/time.ts | 2 +- packages/vx-scale/src/scales/utc.ts | 2 +- packages/vx-scale/test/updateScale.test.ts | 114 +++++++++++++++--- 16 files changed, 119 insertions(+), 35 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index ba651b89a..6257f363f 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -24,7 +24,7 @@ function createScale< DiscreteInput extends StringLike = StringLike, ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( - config: PickScaleConfig<'linear', Output> | PickScaleConfigWithoutType<'linear', Output>, + config?: PickScaleConfig<'linear', Output> | PickScaleConfigWithoutType<'linear', Output>, ): PickD3Scale<'linear', Output>; function createScale< @@ -116,7 +116,7 @@ function createScale< >( config: | ScaleConfig - | PickScaleConfigWithoutType<'linear', Output>, + | PickScaleConfigWithoutType<'linear', Output> = {}, ) { if ('type' in config) { switch (config.type) { diff --git a/packages/vx-scale/src/operators/scaleOperator.ts b/packages/vx-scale/src/operators/scaleOperator.ts index bc8d8602c..96a057371 100644 --- a/packages/vx-scale/src/operators/scaleOperator.ts +++ b/packages/vx-scale/src/operators/scaleOperator.ts @@ -68,11 +68,13 @@ export default function scaleOperator(...ops: OperatorType[ ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput >( scale: PickD3Scale, - config: PickScaleConfigWithoutType, + config?: PickScaleConfigWithoutType, ) { - selectedOps.forEach(op => { - operators[op](scale, config); - }); + if (typeof config !== 'undefined') { + selectedOps.forEach(op => { + operators[op](scale, config); + }); + } return scale; }; diff --git a/packages/vx-scale/src/scales/band.ts b/packages/vx-scale/src/scales/band.ts index 05f37745d..bdb0d1089 100644 --- a/packages/vx-scale/src/scales/band.ts +++ b/packages/vx-scale/src/scales/band.ts @@ -12,7 +12,7 @@ export const updateBandScale = scaleOperator<'band'>( ); export default function createBandScale( - config: PickScaleConfigWithoutType<'band', DefaultOutput, DiscreteInput>, + config?: PickScaleConfigWithoutType<'band', DefaultOutput, DiscreteInput>, ) { return updateBandScale(scaleBand(), config); } diff --git a/packages/vx-scale/src/scales/linear.ts b/packages/vx-scale/src/scales/linear.ts index 075443c3f..d56f690a7 100644 --- a/packages/vx-scale/src/scales/linear.ts +++ b/packages/vx-scale/src/scales/linear.ts @@ -14,7 +14,7 @@ export const updateLinearScale = scaleOperator<'linear'>( ); export default function createLinearScale( - config: PickScaleConfigWithoutType<'linear', Output>, + config?: PickScaleConfigWithoutType<'linear', Output>, ) { return updateLinearScale(scaleLinear(), config); } diff --git a/packages/vx-scale/src/scales/log.ts b/packages/vx-scale/src/scales/log.ts index 098c1ed3c..6e68139c7 100644 --- a/packages/vx-scale/src/scales/log.ts +++ b/packages/vx-scale/src/scales/log.ts @@ -14,7 +14,7 @@ export const updateLogScale = scaleOperator<'log'>( ); export default function createLogScale( - config: PickScaleConfigWithoutType<'log', Output>, + config?: PickScaleConfigWithoutType<'log', Output>, ) { return updateLogScale(scaleLog(), config); } diff --git a/packages/vx-scale/src/scales/ordinal.ts b/packages/vx-scale/src/scales/ordinal.ts index 79648fda6..ea91e0310 100644 --- a/packages/vx-scale/src/scales/ordinal.ts +++ b/packages/vx-scale/src/scales/ordinal.ts @@ -8,6 +8,6 @@ export const updateOrdinalScale = scaleOperator<'ordinal'>('domain', 'range', 'u export default function createOrdinalScale< DiscreteInput extends StringLike = StringLike, Output = DefaultOutput ->(config: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>) { +>(config?: PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput>) { return updateOrdinalScale(scaleOrdinal(), config); } diff --git a/packages/vx-scale/src/scales/point.ts b/packages/vx-scale/src/scales/point.ts index 7fd4a3287..9ab16a39e 100644 --- a/packages/vx-scale/src/scales/point.ts +++ b/packages/vx-scale/src/scales/point.ts @@ -12,7 +12,7 @@ export const updatePointScale = scaleOperator<'point'>( ); export default function createPointScale( - config: PickScaleConfigWithoutType<'point', DefaultOutput, DiscreteInput>, + config?: PickScaleConfigWithoutType<'point', DefaultOutput, DiscreteInput>, ) { return updatePointScale(scalePoint(), config); } diff --git a/packages/vx-scale/src/scales/power.ts b/packages/vx-scale/src/scales/power.ts index ac7c81d5a..fd5fb2596 100644 --- a/packages/vx-scale/src/scales/power.ts +++ b/packages/vx-scale/src/scales/power.ts @@ -15,7 +15,7 @@ export const updatePowScale = scaleOperator<'pow'>( ); export default function createPowScale( - config: PickScaleConfigWithoutType<'pow', Output>, + config?: PickScaleConfigWithoutType<'pow', Output>, ) { return updatePowScale(scalePow(), config); } diff --git a/packages/vx-scale/src/scales/quantile.ts b/packages/vx-scale/src/scales/quantile.ts index cb7cb463c..27b73d3eb 100644 --- a/packages/vx-scale/src/scales/quantile.ts +++ b/packages/vx-scale/src/scales/quantile.ts @@ -6,7 +6,7 @@ import scaleOperator from '../operators/scaleOperator'; export const updateQuantileScale = scaleOperator<'quantile'>('domain', 'range'); export default function createQuantileScale( - config: PickScaleConfigWithoutType<'quantile', Output>, + config?: PickScaleConfigWithoutType<'quantile', Output>, ) { return updateQuantileScale(scaleQuantile(), config); } diff --git a/packages/vx-scale/src/scales/quantize.ts b/packages/vx-scale/src/scales/quantize.ts index 8c799abed..42d21dd73 100644 --- a/packages/vx-scale/src/scales/quantize.ts +++ b/packages/vx-scale/src/scales/quantize.ts @@ -6,7 +6,7 @@ import scaleOperator from '../operators/scaleOperator'; export const updateQuantizeScale = scaleOperator<'quantize'>('domain', 'range', 'nice', 'zero'); export default function createQuantizeScale( - config: PickScaleConfigWithoutType<'quantize', Output>, + config?: PickScaleConfigWithoutType<'quantize', Output>, ) { return updateQuantizeScale(scaleQuantize(), config); } diff --git a/packages/vx-scale/src/scales/squareRoot.ts b/packages/vx-scale/src/scales/squareRoot.ts index f5305cbdf..187ba9be1 100644 --- a/packages/vx-scale/src/scales/squareRoot.ts +++ b/packages/vx-scale/src/scales/squareRoot.ts @@ -14,7 +14,7 @@ export const updateSqrtScale = scaleOperator<'sqrt'>( ); export default function createSqrtScale( - config: PickScaleConfigWithoutType<'sqrt', Output>, + config?: PickScaleConfigWithoutType<'sqrt', Output>, ) { return updateSqrtScale(scaleSqrt(), config); } diff --git a/packages/vx-scale/src/scales/symlog.ts b/packages/vx-scale/src/scales/symlog.ts index 6ba9f8c0c..bf47a2ff4 100644 --- a/packages/vx-scale/src/scales/symlog.ts +++ b/packages/vx-scale/src/scales/symlog.ts @@ -13,7 +13,7 @@ export const updateSymlogScale = scaleOperator<'symlog'>( ); export default function createSymlogScale( - config: PickScaleConfigWithoutType<'symlog', Output>, + config?: PickScaleConfigWithoutType<'symlog', Output>, ) { return updateSymlogScale(scaleSymlog(), config); } diff --git a/packages/vx-scale/src/scales/threshold.ts b/packages/vx-scale/src/scales/threshold.ts index 4c29f9777..8023f8d51 100644 --- a/packages/vx-scale/src/scales/threshold.ts +++ b/packages/vx-scale/src/scales/threshold.ts @@ -9,6 +9,6 @@ export const updateThresholdScale = scaleOperator<'threshold'>('domain', 'range' export default function createThresholdScale< ThresholdInput extends DefaultThresholdInput = DefaultThresholdInput, Output = DefaultOutput ->(config: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>) { +>(config?: PickScaleConfigWithoutType<'threshold', Output, StringLike, ThresholdInput>) { return updateThresholdScale(scaleThreshold(), config); } diff --git a/packages/vx-scale/src/scales/time.ts b/packages/vx-scale/src/scales/time.ts index 057263b38..ae9937489 100644 --- a/packages/vx-scale/src/scales/time.ts +++ b/packages/vx-scale/src/scales/time.ts @@ -13,7 +13,7 @@ export const updateTimeScale = scaleOperator<'time'>( ); export default function createTimeScale( - config: PickScaleConfigWithoutType<'time', Output>, + config?: PickScaleConfigWithoutType<'time', Output>, ) { return updateTimeScale(scaleTime(), config); } diff --git a/packages/vx-scale/src/scales/utc.ts b/packages/vx-scale/src/scales/utc.ts index 8b349eeac..846c11a19 100644 --- a/packages/vx-scale/src/scales/utc.ts +++ b/packages/vx-scale/src/scales/utc.ts @@ -13,7 +13,7 @@ export const updateUtcScale = scaleOperator<'utc'>( ); export default function createUtcScale( - config: PickScaleConfigWithoutType<'utc', Output>, + config?: PickScaleConfigWithoutType<'utc', Output>, ) { return updateUtcScale(scaleUtc(), config); } diff --git a/packages/vx-scale/test/updateScale.test.ts b/packages/vx-scale/test/updateScale.test.ts index 9d1b2bc74..3f2e5d80d 100644 --- a/packages/vx-scale/test/updateScale.test.ts +++ b/packages/vx-scale/test/updateScale.test.ts @@ -1,28 +1,110 @@ -import { updateScale, scaleLinear } from '../src'; +import { + updateScale, + scaleLinear, + scaleLog, + scalePower, + scaleSqrt, + scaleSymlog, + scaleTime, + scaleUtc, + scaleQuantile, + scaleOrdinal, + scalePoint, + scaleBand, + scaleQuantize, + scaleThreshold, +} from '../src'; describe('updateScale', () => { it('should be defined', () => { expect(updateScale).toBeDefined(); }); - it('should return a new copy of the scale', () => { - const domain = [0, 350]; - const range = [0, 2]; - const scale = scaleLinear({ range, domain }); + const scale = scaleLinear(); const nextScale = updateScale(scale); expect(scale).not.toBe(nextScale); }); - - it('should update the new copy of the scale', () => { - const domain = [0, 350]; - const newDomain = [200, 300]; - const range = [0, 2]; - const scale = scaleLinear({ range, domain }); - const nextScale = updateScale(scale, { - domain: newDomain, + it('linear', () => { + const scale = updateScale(scaleLinear(), { domain: [0, 10], range: [2, 4] }); + expect(scale(5)).toEqual(3); + }); + it('log', () => { + const scale = updateScale(scaleLog(), { + base: 2, + domain: [2, 8], + range: [1, 3], }); - expect(scale).not.toBe(nextScale); - expect(nextScale.domain()).toEqual(newDomain); - expect(scale.domain()).toEqual(domain); + expect(scale(4).toFixed(2)).toEqual('2.00'); + }); + it('pow', () => { + const scale = updateScale(scalePower(), { exponent: 2, domain: [1, 3], range: [2, 18] }); + expect(scale(2)).toEqual(8); + }); + it('sqrt', () => { + const scale = updateScale(scaleSqrt(), { domain: [1, 9], range: [1, 3] }); + expect(scale(4)).toEqual(2); + }); + it('symlog', () => { + const scale = updateScale(scaleSymlog(), { domain: [1, 9], range: [1, 3], constant: 2 }); + expect(scale(4).toFixed(2)).toEqual('2.07'); + }); + it('time', () => { + const scale = updateScale(scaleTime(), { + domain: [new Date(2020, 0, 1), new Date(2020, 0, 10)], + range: [1, 10], + }); + expect(scale(new Date(2020, 0, 4))).toEqual(4); + }); + it('utc', () => { + const scale = updateScale(scaleUtc(), { + domain: [new Date(Date.UTC(2020, 0, 1)), new Date(Date.UTC(2020, 0, 10))], + range: [1, 10], + }); + expect(scale(new Date(Date.UTC(2020, 0, 4)))).toEqual(4); + }); + it('quantile', () => { + const scale = updateScale(scaleQuantile(), { domain: [1, 3, 5, 7], range: [0, 10] }); + expect(scale(2)).toEqual(0); + }); + it('quantize', () => { + const scale = updateScale(scaleQuantize(), { domain: [1, 10], range: ['red', 'green'] }); + expect(scale(2)).toEqual('red'); + expect(scale(6)).toEqual('green'); + }); + it('threshold', () => { + const scale = updateScale(scaleThreshold(), { + domain: [0, 1] as number[], + range: ['red', 'white', 'green'], + }); + expect(scale(-1)).toEqual('red'); + expect(scale(0)).toEqual('white'); + expect(scale(0.5)).toEqual('white'); + expect(scale(1)).toEqual('green'); + expect(scale(1000)).toEqual('green'); + }); + it('ordinal', () => { + const scale = updateScale(scaleOrdinal(), { domain: ['pig', 'cat'], range: ['red', 'green'] }); + expect(scale('pig')).toEqual('red'); + expect(scale('cat')).toEqual('green'); + }); + it('point', () => { + const scale = updateScale(scalePoint(), { + domain: ['a', 'b', 'c'], + range: [1.1, 3.5], + round: true, + }); + expect(scale('a')).toEqual(1); + expect(scale('b')).toEqual(2); + expect(scale('c')).toEqual(3); + }); + it('band', () => { + const scale = updateScale(scaleBand(), { + domain: ['a', 'b', 'c'], + range: [1.1, 3.5], + round: false, + }); + expect(scale('a')).toEqual(1.1); + expect(scale('b')).toEqual(1.9); + expect(scale('c')).toEqual(2.7); }); }); From e9eb41cb3a02210fb57c5b085ddb4fe00ada3777 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 18:13:40 -0700 Subject: [PATCH 36/42] test: more unit tests --- packages/vx-scale/src/createScale.ts | 6 +++--- packages/vx-scale/src/updateScale.ts | 3 +-- packages/vx-scale/test/createScale.test.ts | 4 ++++ packages/vx-scale/test/updateScale.test.ts | 7 +++++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/vx-scale/src/createScale.ts b/packages/vx-scale/src/createScale.ts index 6257f363f..1713ddb22 100644 --- a/packages/vx-scale/src/createScale.ts +++ b/packages/vx-scale/src/createScale.ts @@ -114,11 +114,11 @@ function createScale< DiscreteInput extends StringLike, ThresholdInput extends DefaultThresholdInput >( - config: + config?: | ScaleConfig - | PickScaleConfigWithoutType<'linear', Output> = {}, + | PickScaleConfigWithoutType<'linear', Output>, ) { - if ('type' in config) { + if (typeof config !== 'undefined' && 'type' in config) { switch (config.type) { case 'linear': return createLinearScale(config); diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index e5432520a..e32722a4d 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -171,6 +171,7 @@ function updateScale< // Just cast the scale and config to the correct types. switch (type) { + default: case 'linear': return updateLinearScale( scaleOut as PickD3Scale<'linear', Output, DiscreteInput, ThresholdInput>, @@ -236,8 +237,6 @@ function updateScale< scaleOut as PickD3Scale<'band', Output, DiscreteInput, ThresholdInput>, config as PickScaleConfigWithoutType<'band', Output, DiscreteInput, ThresholdInput>, ); - default: - return scaleOut; } } diff --git a/packages/vx-scale/test/createScale.test.ts b/packages/vx-scale/test/createScale.test.ts index bd1295978..012d84f62 100644 --- a/packages/vx-scale/test/createScale.test.ts +++ b/packages/vx-scale/test/createScale.test.ts @@ -94,4 +94,8 @@ describe('createScale()', () => { expect(scale('b')).toEqual(1.9); expect(scale('c')).toEqual(2.7); }); + it('invalid type', () => { + // @ts-ignore + expect(createScale({ type: 'invalid' })).toBeDefined(); + }); }); diff --git a/packages/vx-scale/test/updateScale.test.ts b/packages/vx-scale/test/updateScale.test.ts index 3f2e5d80d..c81307d83 100644 --- a/packages/vx-scale/test/updateScale.test.ts +++ b/packages/vx-scale/test/updateScale.test.ts @@ -1,3 +1,4 @@ +import TimezoneMock from 'timezone-mock'; import { updateScale, scaleLinear, @@ -49,11 +50,13 @@ describe('updateScale', () => { expect(scale(4).toFixed(2)).toEqual('2.07'); }); it('time', () => { + TimezoneMock.register('US/Pacific'); const scale = updateScale(scaleTime(), { domain: [new Date(2020, 0, 1), new Date(2020, 0, 10)], range: [1, 10], }); expect(scale(new Date(2020, 0, 4))).toEqual(4); + TimezoneMock.unregister(); }); it('utc', () => { const scale = updateScale(scaleUtc(), { @@ -107,4 +110,8 @@ describe('updateScale', () => { expect(scale('b')).toEqual(1.9); expect(scale('c')).toEqual(2.7); }); + it('invalid type', () => { + // @ts-ignore + expect(updateScale(scaleLinear(), { type: 'invalid' })).toBeDefined(); + }); }); From 9e24d658f46bc5b347b353a2dbf504c85388e4c7 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 18:20:24 -0700 Subject: [PATCH 37/42] test: 100% statement coverage --- package.json | 1 + packages/vx-scale/test/scaleLinear.test.ts | 12 ++++++++++++ yarn.lock | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/package.json b/package.json index 5808934a8..9e173c299 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "enzyme-to-json": "^3.4.0", "fs-jetpack": "^1.3.0", "husky": "^3.0.0", + "jest-mock-console": "^1.0.1", "lerna": "^3.15.0", "marked": "^0.7.0", "raf": "^3.4.0", diff --git a/packages/vx-scale/test/scaleLinear.test.ts b/packages/vx-scale/test/scaleLinear.test.ts index b6f55c570..d584f4dae 100644 --- a/packages/vx-scale/test/scaleLinear.test.ts +++ b/packages/vx-scale/test/scaleLinear.test.ts @@ -1,3 +1,4 @@ +import mockConsole from 'jest-mock-console'; import { scaleLinear } from '../src'; describe('scaleLinear()', () => { @@ -74,6 +75,17 @@ describe('scaleLinear()', () => { expect(scale(2.2)).toEqual(2.2); expect(scale(2.6)).toEqual(2.6); }); + it('warns if do both interpolate and round', () => { + const restoreConsole = mockConsole(); + scaleLinear({ + domain: [0, 10], + range: [0, 10], + interpolate: 'hsl', + round: true, + }); + expect(console.warn).toHaveBeenCalledTimes(1); + restoreConsole(); + }); }); describe('set zero', () => { it('true', () => { diff --git a/yarn.lock b/yarn.lock index ecb1a5172..20959cc91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8292,6 +8292,11 @@ jest-message-util@^25.5.0: slash "^3.0.0" stack-utils "^1.0.1" +jest-mock-console@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/jest-mock-console/-/jest-mock-console-1.0.1.tgz#07978047735a782d0d4172d1afcabd82f6de9b08" + integrity sha512-Bn+Of/cvz9LOEEeEg5IX5Lsf8D2BscXa3Zl5+vSVJl37yiT8gMAPPKfE09jJOwwu1zbagL11QTrH+L/Gn8udOg== + jest-mock@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.5.0.tgz#a91a54dabd14e37ecd61665d6b6e06360a55387a" From 70a9a6665b2be6d219d8f55173de5cc2851ac92f Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 18:26:27 -0700 Subject: [PATCH 38/42] refactor: simplify updateScale --- packages/vx-scale/src/updateScale.ts | 99 ++-------------------------- 1 file changed, 4 insertions(+), 95 deletions(-) diff --git a/packages/vx-scale/src/updateScale.ts b/packages/vx-scale/src/updateScale.ts index e32722a4d..2a212fb54 100644 --- a/packages/vx-scale/src/updateScale.ts +++ b/packages/vx-scale/src/updateScale.ts @@ -1,20 +1,9 @@ import { PickScaleConfigWithoutType, ScaleConfigWithoutType } from './types/ScaleConfig'; import { DefaultThresholdInput, D3Scale, PickD3Scale } from './types/Scale'; import { StringLike, DefaultOutput } from './types/Base'; -import { updateThresholdScale } from './scales/threshold'; -import { updateQuantileScale } from './scales/quantile'; -import { updateBandScale } from './scales/band'; -import { updatePointScale } from './scales/point'; -import { updateOrdinalScale } from './scales/ordinal'; -import { updateLogScale } from './scales/log'; -import { updateSymlogScale } from './scales/symlog'; -import { updatePowScale } from './scales/power'; -import { updateQuantizeScale } from './scales/quantize'; -import { updateTimeScale } from './scales/time'; -import { updateLinearScale } from './scales/linear'; -import { updateSqrtScale } from './scales/squareRoot'; -import { updateUtcScale } from './scales/utc'; -import inferScaleType from './utils/inferScaleType'; +import scaleOperator, { ALL_OPERATORS } from './operators/scaleOperator'; + +const applyAllOperators = scaleOperator(...ALL_OPERATORS); // Overload function signature for more strict typing, e.g., // If the scale is a ScaleLinear, the config is a linear config. @@ -157,87 +146,7 @@ function updateScale< scale: D3Scale, config?: ScaleConfigWithoutType, ) { - const scaleOut = scale.copy() as D3Scale; - - // If a config is not specified, just return a copy - if (typeof config === 'undefined') { - return scaleOut; - } - - const type = inferScaleType(scale); - - // Function overloading above should ensure the scale and config - // are compatible matches. - // Just cast the scale and config to the correct types. - - switch (type) { - default: - case 'linear': - return updateLinearScale( - scaleOut as PickD3Scale<'linear', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'linear', Output, DiscreteInput, ThresholdInput>, - ); - case 'log': - return updateLogScale( - scaleOut as PickD3Scale<'log', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'log', Output, DiscreteInput, ThresholdInput>, - ); - case 'pow': - return updatePowScale( - scaleOut as PickD3Scale<'pow', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'pow', Output, DiscreteInput, ThresholdInput>, - ); - case 'sqrt': - return updateSqrtScale( - scaleOut as PickD3Scale<'sqrt', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'sqrt', Output, DiscreteInput, ThresholdInput>, - ); - case 'symlog': - return updateSymlogScale( - scaleOut as PickD3Scale<'symlog', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'symlog', Output, DiscreteInput, ThresholdInput>, - ); - case 'time': - return updateTimeScale( - scaleOut as PickD3Scale<'time', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'time', Output, DiscreteInput, ThresholdInput>, - ); - case 'utc': - return updateUtcScale( - scaleOut as PickD3Scale<'utc', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'utc', Output, DiscreteInput, ThresholdInput>, - ); - case 'quantile': - return updateQuantileScale( - scaleOut as PickD3Scale<'quantile', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'quantile', Output, DiscreteInput, ThresholdInput>, - ); - case 'quantize': - return updateQuantizeScale( - scaleOut as PickD3Scale<'quantize', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'quantize', Output, DiscreteInput, ThresholdInput>, - ); - case 'threshold': - return updateThresholdScale( - scaleOut as PickD3Scale<'threshold', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'threshold', Output, DiscreteInput, ThresholdInput>, - ); - case 'ordinal': - return updateOrdinalScale( - scaleOut as PickD3Scale<'ordinal', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'ordinal', Output, DiscreteInput, ThresholdInput>, - ); - case 'point': - return updatePointScale( - scaleOut as PickD3Scale<'point', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'point', Output, DiscreteInput, ThresholdInput>, - ); - case 'band': - return updateBandScale( - scaleOut as PickD3Scale<'band', Output, DiscreteInput, ThresholdInput>, - config as PickScaleConfigWithoutType<'band', Output, DiscreteInput, ThresholdInput>, - ); - } + return applyAllOperators(scale.copy(), config); } export default updateScale; From 5c2f2e92eb27489b084f51f12d8e1ab121315bee Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 18:39:36 -0700 Subject: [PATCH 39/42] test: last unit test --- packages/vx-scale/test/scaleTime.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/vx-scale/test/scaleTime.test.ts b/packages/vx-scale/test/scaleTime.test.ts index b6069d842..71673bf01 100644 --- a/packages/vx-scale/test/scaleTime.test.ts +++ b/packages/vx-scale/test/scaleTime.test.ts @@ -67,6 +67,10 @@ describe('scaleTime()', () => { const scale = scaleTime({ domain: unniceDomain, nice: { interval: 'hour', step: 3 } }); expect(scale.domain()).toEqual([new Date(2020, 0, 1), new Date(2020, 0, 9, 21)]); }); + it('invalid nice object', () => { + const scale = scaleTime({ domain: unniceDomain, nice: { interval: 'hour', step: NaN } }); + expect(scale.domain()).toEqual(unniceDomain); + }); }); describe('set round', () => { it('true', () => { From fe8d0b2b1ccfd6a44016bf8a61314e714a1d871d Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 23 Jul 2020 18:43:15 -0700 Subject: [PATCH 40/42] test: zero test --- packages/vx-scale/test/scaleLinear.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vx-scale/test/scaleLinear.test.ts b/packages/vx-scale/test/scaleLinear.test.ts index d584f4dae..39cb53f2c 100644 --- a/packages/vx-scale/test/scaleLinear.test.ts +++ b/packages/vx-scale/test/scaleLinear.test.ts @@ -91,6 +91,7 @@ describe('scaleLinear()', () => { it('true', () => { expect(scaleLinear({ domain: [1, 2], zero: true }).domain()).toEqual([0, 2]); expect(scaleLinear({ domain: [-2, -1], zero: true }).domain()).toEqual([-2, 0]); + expect(scaleLinear({ domain: [1, -2], zero: true }).domain()).toEqual([1, -2]); expect(scaleLinear({ domain: [-2, 3], zero: true }).domain()).toEqual([-2, 3]); }); it('false', () => { From a13314ace56e67fc25e9493f11467a8ca734f557 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Fri, 24 Jul 2020 08:42:44 -0700 Subject: [PATCH 41/42] docs: update warning text --- packages/vx-scale/Readme.md | 16 ++++++++-------- packages/vx-scale/src/operators/round.ts | 5 ++++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/vx-scale/Readme.md b/packages/vx-scale/Readme.md index 169541efa..c1dbfe58c 100644 --- a/packages/vx-scale/Readme.md +++ b/packages/vx-scale/Readme.md @@ -65,7 +65,7 @@ Example: const scale = Scale.scaleBand({ /* range, - rangeRound, + round, domain, padding, nice = false @@ -83,7 +83,7 @@ Example: const scale = Scale.scaleLinear({ /* range, - rangeRound, + round, domain, nice = false, clamp = false, @@ -101,7 +101,7 @@ Example: const scale = Scale.scaleLog({ /* range, - rangeRound, + round, domain, base, nice = false, @@ -136,7 +136,7 @@ Example: const scale = Scale.scalePoint({ /* range, - rangeRound, + round, domain, padding, align, @@ -155,7 +155,7 @@ Example: const scale = Scale.scalePower({ /* range, - rangeRound, + round, domain, exponent, nice = false, @@ -175,7 +175,7 @@ Example: const scale = Scale.scaleSqrt({ /* range, - rangeRound, + round, domain, nice = false, clamp = false, @@ -193,7 +193,7 @@ Example: const scale = Scale.scaleTime({ /* range, - rangeRound, + round, domain, nice = false, clamp = false, @@ -209,7 +209,7 @@ Example: const scale = Scale.scaleUtc({ /* range, - rangeRound, + round, domain, nice = false, clamp = false, diff --git a/packages/vx-scale/src/operators/round.ts b/packages/vx-scale/src/operators/round.ts index 1c64ff258..62f121833 100644 --- a/packages/vx-scale/src/operators/round.ts +++ b/packages/vx-scale/src/operators/round.ts @@ -14,7 +14,10 @@ export default function applyRound< ) { if ('round' in config && typeof config.round !== 'undefined') { if (config.round && 'interpolate' in config && typeof config.interpolate !== 'undefined') { - console.warn(`can't round and interpolate`); + console.warn( + `[vx/scale/applyRound] ignoring round: scale config contains round and interpolate. only applying interpolate. config:`, + config, + ); } else if ('round' in scale) { scale.round(config.round); } else if ('interpolate' in scale && config.round) { From 27f6836121d0ed51636346ffe16437049a05485e Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Fri, 24 Jul 2020 10:41:18 -0700 Subject: [PATCH 42/42] docs: add comment --- packages/vx-scale/src/operators/round.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/vx-scale/src/operators/round.ts b/packages/vx-scale/src/operators/round.ts index 62f121833..2703aac10 100644 --- a/packages/vx-scale/src/operators/round.ts +++ b/packages/vx-scale/src/operators/round.ts @@ -19,8 +19,13 @@ export default function applyRound< config, ); } else if ('round' in scale) { + // for point and band scales scale.round(config.round); } else if ('interpolate' in scale && config.round) { + // for continuous output scales + // setting config.round = true + // is actually setting interpolator to interpolateRound + // as these scales do not have scale.round() function scale.interpolate((interpolateRound as unknown) as InterpolatorFactory); } }