diff --git a/packages/nivo-scales/package.json b/packages/nivo-scales/package.json
index 4f1c6bf4c..d25bd3a45 100644
--- a/packages/nivo-scales/package.json
+++ b/packages/nivo-scales/package.json
@@ -32,8 +32,6 @@
},
"scripts": {
"lint": "eslint src stories tests",
- "test": "jest --verbose ./tests",
- "test:cover": "jest --verbose --coverage ./tests",
"build:commonjs": "rm -rf lib && cross-env NODE_ENV=commonjs babel src --out-dir lib",
"build:commonjs:watch": "npm run build:commonjs -- --watch",
"build:es": "rm -rf es && cross-env NODE_ENV=es babel src --out-dir es",
diff --git a/packages/nivo-scales/src/Scales.js b/packages/nivo-scales/src/Scales.js
index 8651eca5a..db6cbbc9e 100644
--- a/packages/nivo-scales/src/Scales.js
+++ b/packages/nivo-scales/src/Scales.js
@@ -12,16 +12,17 @@ import withPropsOnChange from 'recompose/withPropsOnChange'
import setDisplayName from 'recompose/setDisplayName'
import pure from 'recompose/pure'
import PropTypes from 'prop-types'
-import { LinearScalePropType, PointScalePropType } from './propsTypes'
-import { scalesFromConfig } from './compute'
+import { LinearScalePropType, PointScalePropType, TimeScalePropType } from './propsTypes'
+import { scalesFromConfig } from './scaleByType'
const Scales = ({ computedScales, children }) => {children(computedScales)}
Scales.propTypes = {
children: PropTypes.func.isRequired,
computedScales: PropTypes.object.isRequired,
- scales: PropTypes.arrayOf(PropTypes.oneOfType([LinearScalePropType, PointScalePropType]))
- .isRequired,
+ scales: PropTypes.arrayOf(
+ PropTypes.oneOfType([LinearScalePropType, PointScalePropType, TimeScalePropType])
+ ).isRequired,
}
const enhance = compose(
diff --git a/packages/nivo-scales/src/index.js b/packages/nivo-scales/src/index.js
index 3dfe463fe..02c52811f 100644
--- a/packages/nivo-scales/src/index.js
+++ b/packages/nivo-scales/src/index.js
@@ -7,4 +7,6 @@
* file that was distributed with this source code.
*/
export { default as Scales } from './Scales'
-export * from './compute'
+export * from './linearScale'
+export * from './pointScale'
+export * from './scaleByType'
diff --git a/packages/nivo-scales/src/compute.js b/packages/nivo-scales/src/linearScale.js
similarity index 54%
rename from packages/nivo-scales/src/compute.js
rename to packages/nivo-scales/src/linearScale.js
index 5fca4017f..a5908b73b 100644
--- a/packages/nivo-scales/src/compute.js
+++ b/packages/nivo-scales/src/linearScale.js
@@ -8,9 +8,20 @@
*/
import getMin from 'lodash/min'
import getMax from 'lodash/max'
-import uniq from 'lodash/uniq'
-import { scaleLinear, scalePoint } from 'd3-scale'
+import { scaleLinear } from 'd3-scale'
+/**
+ * Compute a linear scale from config and data set.
+ *
+ * @param {Array.} data
+ * @param {'auto'|number} min
+ * @param {'auto'|number} max
+ * @param {string|number} property
+ * @param {Array.} range
+ * @param {boolean} stacked
+ *
+ * @return {Object}
+ */
export const computeLinearScale = ({
data,
min = 'auto',
@@ -47,44 +58,3 @@ export const computeLinearScale = ({
.domain([domainMin, domainMax])
.range(range)
}
-
-export const computePointScale = ({
- data,
- domain: _domain,
- range,
- property,
- checkConsistency = false,
-}) => {
- if (checkConsistency === true) {
- const uniqLengths = uniq(data.map(({ data }) => data.length))
- if (uniqLengths.length > 1) {
- throw new Error(
- [
- `Found inconsistent data for '${property}',`,
- `expecting all series to have same length`,
- `but found: ${uniqLengths.join(', ')}`,
- ].join(' ')
- )
- }
- }
-
- const domain = _domain !== undefined ? _domain : data[0].map(d => d[property])
-
- return scalePoint()
- .range(range)
- .domain(domain)
-}
-
-const computeFunctionByType = {
- linear: computeLinearScale,
- point: computePointScale,
-}
-
-export const scalesFromConfig = scaleConfigs => {
- const computedScales = {}
- scaleConfigs.forEach(scaleConfig => {
- computedScales[scaleConfig.id] = computeFunctionByType[scaleConfig.type](scaleConfig)
- })
-
- return computedScales
-}
diff --git a/packages/nivo-scales/src/pointScale.js b/packages/nivo-scales/src/pointScale.js
new file mode 100644
index 000000000..3bde3e3de
--- /dev/null
+++ b/packages/nivo-scales/src/pointScale.js
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the nivo project.
+ *
+ * Copyright 2016-present, Raphaël Benitte.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+import uniq from 'lodash/uniq'
+import { scalePoint } from 'd3-scale'
+
+export const computePointScale = ({
+ data,
+ domain: _domain,
+ range,
+ property,
+ checkConsistency = false,
+}) => {
+ if (checkConsistency === true) {
+ const uniqLengths = uniq(data.map(({ data }) => data.length))
+ if (uniqLengths.length > 1) {
+ throw new Error(
+ [
+ `Found inconsistent data for '${property}',`,
+ `expecting all series to have same length`,
+ `but found: ${uniqLengths.join(', ')}`,
+ ].join(' ')
+ )
+ }
+ }
+
+ const domain = _domain !== undefined ? _domain : data[0].map(d => d[property])
+
+ return scalePoint()
+ .range(range)
+ .domain(domain)
+}
diff --git a/packages/nivo-scales/src/propsTypes.js b/packages/nivo-scales/src/propsTypes.js
index ffcbcc5a3..7a8c1fafb 100644
--- a/packages/nivo-scales/src/propsTypes.js
+++ b/packages/nivo-scales/src/propsTypes.js
@@ -25,3 +25,12 @@ export const PointScalePropType = PropTypes.shape({
domain: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])),
range: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])).isRequired,
})
+
+export const TimeScalePropType = PropTypes.shape({
+ type: PropTypes.oneOf(['time']),
+ data: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.object)).isRequired,
+ property: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
+ min: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number]),
+ max: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number]),
+ range: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number])).isRequired,
+})
diff --git a/packages/nivo-scales/src/scaleByType.js b/packages/nivo-scales/src/scaleByType.js
new file mode 100644
index 000000000..efe680464
--- /dev/null
+++ b/packages/nivo-scales/src/scaleByType.js
@@ -0,0 +1,26 @@
+/*
+ * This file is part of the nivo project.
+ *
+ * Copyright 2016-present, Raphaël Benitte.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+import { computeLinearScale } from './linearScale'
+import { computePointScale } from './pointScale'
+import { computeTimeScale } from './timeScale'
+
+export const scaleByType = {
+ linear: computeLinearScale,
+ point: computePointScale,
+ time: computeTimeScale,
+}
+
+export const scalesFromConfig = scaleConfigs => {
+ const computedScales = {}
+ scaleConfigs.forEach(scaleConfig => {
+ computedScales[scaleConfig.id] = scaleByType[scaleConfig.type](scaleConfig)
+ })
+
+ return computedScales
+}
diff --git a/packages/nivo-scales/src/timeScale.js b/packages/nivo-scales/src/timeScale.js
new file mode 100644
index 000000000..78ed5ee96
--- /dev/null
+++ b/packages/nivo-scales/src/timeScale.js
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the nivo project.
+ *
+ * Copyright 2016-present, Raphaël Benitte.
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+import uniq from 'lodash/uniq'
+import { scaleTime } from 'd3-scale'
+
+/**
+ * Add auto date conversion to d3 `timeScale`,
+ * it helps to be able to pas raw data without having
+ * to deal with Date objects and pre-process the data.
+ *
+ * @param {Object} scale
+ *
+ * @return {Object}
+ */
+const enhanceScale = scale => {
+ const enhancedScale = function(dateStr) {
+ return scale(new Date(dateStr))
+ }
+
+ // @todo: fix copy()
+ Object.keys(scale).forEach(key => {
+ enhancedScale[key] = scale[key].bind(scale)
+ })
+
+ return enhancedScale
+}
+
+/**
+ * Compute a time scale from config and data set.
+ *
+ * @param {Array.} data
+ * @param {'auto'|string} min
+ * @param {'auto'|string} max
+ * @param {string|number} property
+ * @param {Array.} range
+ *
+ * @return {Object}
+ */
+export const computeTimeScale = ({ data, min = 'auto', max = 'auto', property, range }) => {
+ let domainMin = min !== 'auto' ? new Date(min) : min
+ let domainMax = max !== 'auto' ? new Date(max) : max
+
+ if (min === 'auto' || max === 'auto') {
+ const allDates = uniq(
+ data.reduce((agg, serie) => [...agg, ...serie.map(d => d[property])], [])
+ )
+ .map(dateStr => new Date(dateStr))
+ .sort((a, b) => b - a)
+ .reverse()
+
+ if (min === 'auto') domainMin = allDates[0]
+ if (max === 'auto') domainMax = allDates[allDates.length - 1]
+ }
+
+ const scale = scaleTime()
+ .domain([domainMin, domainMax])
+ .range(range)
+
+ return enhanceScale(scale)
+}
diff --git a/website/src/components/charts/line/LineCanvas/generateData.js b/website/src/components/charts/line/LineCanvas/generateData.js
index c5c27c8ce..b7fbc8ae1 100644
--- a/website/src/components/charts/line/LineCanvas/generateData.js
+++ b/website/src/components/charts/line/LineCanvas/generateData.js
@@ -26,6 +26,9 @@ export default () => {
yRand: Math.random() * 2,
easing: 'random',
xGaps: gaps[key] || [],
- }),
+ }).map(d => ({
+ ...d,
+ y: d.y < 0 ? 0 : d.y,
+ })),
}))
}