diff --git a/packages/bar/index.d.ts b/packages/bar/index.d.ts index a67b5f00b..6432d246a 100644 --- a/packages/bar/index.d.ts +++ b/packages/bar/index.d.ts @@ -114,11 +114,10 @@ declare module '@nivo/bar' { legendOffset: number legendPosition: 'start' | 'center' | 'end' orient: 'top' | 'right' | 'bottom' | 'left' - tickCount: number tickPadding: number tickRotation: number tickSize: number - tickValues: string[] | number[] + tickValues: number | string[] | number[] }> export type BarSvgProps = Data diff --git a/packages/core/src/components/axes/Axis.js b/packages/core/src/components/axes/Axis.js index 3e5d745a2..86a073b2e 100644 --- a/packages/core/src/components/axes/Axis.js +++ b/packages/core/src/components/axes/Axis.js @@ -26,8 +26,12 @@ const legendPositions = ['start', 'center', 'end'] export const axisPropType = PropTypes.shape({ orient: PropTypes.oneOf(axisPositions), - // ticks - tickValues: PropTypes.array, + tickValues: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.instanceOf(Date)]) + ), + ]), tickSize: PropTypes.number, tickPadding: PropTypes.number, tickRotation: PropTypes.number, @@ -60,7 +64,6 @@ const Axis = ({ // ticks tickValues, - tickCount, tickSize, tickPadding, tickRotation, @@ -88,7 +91,6 @@ const Axis = ({ scale, position: _position, tickValues, - tickCount, tickSize, tickPadding, tickRotation, @@ -218,41 +220,38 @@ const Axis = ({ } Axis.propTypes = { - // generic width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, position: PropTypes.oneOf(axisPositions).isRequired, scale: PropTypes.func.isRequired, - // ticks - tickValues: PropTypes.array, - tickCount: PropTypes.number, + tickValues: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.number, PropTypes.string, PropTypes.instanceOf(Date)]) + ), + ]), tickSize: PropTypes.number.isRequired, tickPadding: PropTypes.number.isRequired, tickRotation: PropTypes.number.isRequired, format: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), - // legend legend: PropTypes.node, legendPosition: PropTypes.oneOf(legendPositions).isRequired, legendOffset: PropTypes.number.isRequired, - // theming theme: PropTypes.object.isRequired, - // interactivity onClick: PropTypes.func, ...motionPropTypes, } Axis.defaultProps = { - // ticks tickSize: 5, tickPadding: 5, tickRotation: 0, - // legend legendPosition: 'end', legendOffset: 0, } diff --git a/packages/core/src/lib/cartesian/axes.js b/packages/core/src/lib/cartesian/axes.js index fba55f5a5..37509a0b2 100644 --- a/packages/core/src/lib/cartesian/axes.js +++ b/packages/core/src/lib/cartesian/axes.js @@ -6,6 +6,8 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ +import isArray from 'lodash/isArray' +import isNumber from 'lodash/isNumber' import { textPropsByEngine } from '../bridge' const horizontalPositions = ['top', 'bottom'] @@ -51,16 +53,15 @@ const getScaleValues = (scale, tickCount) => { */ /** - * @param {number} width - * @param {number} height - * @param {string} _position - * @param {Object} scale - * @param {Array.} [tickValues] - * @param {number} [tickCount] - * @param {number} [tickSize=5] - * @param {number} [tickPadding=5] - * @param {number} [tickRotation=0] - * @parem {string} [engine='svg'] + * @param {number} width + * @param {number} height + * @param {string} _position + * @param {Object} scale + * @param {number|Array.} [_tickValues] + * @param {number} [tickSize=5] + * @param {number} [tickPadding=5] + * @param {number} [tickRotation=0] + * @parem {string} [engine='svg'] * * @return {{ x: number, y: number, ticks: Array., textAlign: string, textBaseline: string }} */ @@ -70,16 +71,16 @@ export const computeAxisTicks = ({ position: _position, scale, - // ticks - tickValues, - tickCount, + tickValues: _tickValues, tickSize = 5, tickPadding = 5, tickRotation = 0, - //format, engine = 'svg', }) => { + const tickValues = isArray(_tickValues) ? _tickValues : undefined + const tickCount = isNumber(_tickValues) ? _tickValues : undefined + const values = tickValues || getScaleValues(scale, tickCount) const textProps = textPropsByEngine[engine] diff --git a/packages/core/tests/lib/cartesian/axes.test.js b/packages/core/tests/lib/cartesian/axes.test.js index f60d2562a..dc12b0ccd 100644 --- a/packages/core/tests/lib/cartesian/axes.test.js +++ b/packages/core/tests/lib/cartesian/axes.test.js @@ -85,7 +85,7 @@ describe('computeAxisTicks()', () => { scale: linearScale, width, height, - tickCount: 1, + tickValues: 1, position: 'left', }) expect(axis.ticks.length).toBe(2) diff --git a/website/src/SiteMap.js b/website/src/SiteMap.js index 510527ffa..4406ba8f5 100644 --- a/website/src/SiteMap.js +++ b/website/src/SiteMap.js @@ -61,6 +61,7 @@ import WafflePage from './components/charts/waffle/WafflePage' import Waffle from './components/charts/waffle/Waffle' import WaffleHtml from './components/charts/waffle/WaffleHtml' import WaffleCanvas from './components/charts/waffle/WaffleCanvas' +import Axes from './components/guides/axes/Axes' import Colors from './components/guides/colors/Colors' import Legends from './components/guides/legends/Legends' import Gradients from './components/guides/gradients/Gradients' @@ -475,6 +476,13 @@ const SITEMAP = [ label: 'Guides', // those items must not be nested children: [ + { + className: 'axes', + path: '/guides/axes', + label: 'Axes', + component: Axes, + description: 'Configuring axes for nivo components.', + }, { className: 'colors', path: '/guides/colors', diff --git a/website/src/components/controls/TextControl.js b/website/src/components/controls/TextControl.js index 38b74e31e..2c9c5d7c4 100644 --- a/website/src/components/controls/TextControl.js +++ b/website/src/components/controls/TextControl.js @@ -19,10 +19,16 @@ export default class TextControl extends Component { return (
- -
) diff --git a/website/src/components/guides/axes/Axes.js b/website/src/components/guides/axes/Axes.js new file mode 100644 index 000000000..5030525a0 --- /dev/null +++ b/website/src/components/guides/axes/Axes.js @@ -0,0 +1,48 @@ +/* + * 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 React, { Component } from 'react' +import Helmet from 'react-helmet' +import { Link } from 'react-router-dom' +import AxesPosition from './AxesPosition' +import AxesTicks from './AxesTicks' +import AxesLegend from './AxesLegend' + +export default class Axes extends Component { + render() { + return ( +
+ +
+
+

Axes

+
+
+
+

Using axes in nivo components

+

+ Axes are built on top of{' '} + + d3 scales + + . A lot of nivo components make use of it (Bar,{' '} + Line, ScatterPlot + …). +

+
+ + + +
+ ) + } +} diff --git a/website/src/components/guides/axes/AxesLegend.js b/website/src/components/guides/axes/AxesLegend.js new file mode 100644 index 000000000..96f335db0 --- /dev/null +++ b/website/src/components/guides/axes/AxesLegend.js @@ -0,0 +1,83 @@ +/* + * 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 React, { Component, Fragment } from 'react' +import { Axis, defaultTheme } from '@nivo/core' +import { linearXScale, linearYScale } from './scales' + +const axisPositions = ['start', 'center', 'end'] + +export default class AxesLegend extends Component { + render() { + return ( + +
+

Axis legend

+

+ You can optionally add a legend to an axis by setting the value of the{' '} + legend property. +

+

Legend position

+

+ Legend position is controlled by two properties, legendPosition{' '} + and legendOffset.legendPosition must be one of:{' '} + start, center or end,{' '} + legendOffset will affect y position for top{' '} + and bottom axes and x position for left{' '} + and right axes. +

+
+
+
+ + {axisPositions.map((position, i) => ( + + + + ))} + + + {axisPositions.map((position, i) => ( + + + + ))} + +
+
+
+ ) + } +} diff --git a/website/src/components/guides/axes/AxesPosition.js b/website/src/components/guides/axes/AxesPosition.js new file mode 100644 index 000000000..49a5a67e7 --- /dev/null +++ b/website/src/components/guides/axes/AxesPosition.js @@ -0,0 +1,65 @@ +/* + * 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 React, { Component, Fragment } from 'react' +import { Axes, defaultTheme } from '@nivo/core' +import { linearXScale, linearYScale } from './scales' + +export default class AxesPosition extends Component { + render() { + return ( + +
+

Axis position

+

+ Axis position is determined by the property you use{' '} + (top|right|bottom|left)Axis. +

+
+
+
+ + + + + +
+
+
+ ) + } +} diff --git a/website/src/components/guides/axes/AxesTicks.js b/website/src/components/guides/axes/AxesTicks.js new file mode 100644 index 000000000..8db12e061 --- /dev/null +++ b/website/src/components/guides/axes/AxesTicks.js @@ -0,0 +1,154 @@ +/* + * 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 React, { Component, Fragment } from 'react' +import { Axis, defaultTheme } from '@nivo/core' +import { linearXScale, pointXScale, timeXScale } from './scales' + +export default class AxesTicks extends Component { + render() { + return ( + +
+

Axis ticks

+

+ Axes are composed of ticks, you can control the way they + are computed and their aspect. By default, computing which ticks are + displayed is managed by the corresponding{' '} + + d3 scale + + . +

+

Ticks values

+

+ You can use the tickValues property in order to customize the + displayed ticks, it accepts several forms: +

+
    +
  • + an array of values, which should be numbers for a linear scale, values + being part of the dataset for point scales, or dates for time scales. +
  • +
  • + a number which define the number of ticks to display,{' '} + doesn't work with point scale, also note that when + using this form{' '} + + it's not guaranteed that you'll have exactly the number of ticks you + asked for + {' '} + as d3 will make an educated guess to define them. +
  • +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
+
+
+ ) + } +} diff --git a/website/src/components/guides/axes/scales.js b/website/src/components/guides/axes/scales.js new file mode 100644 index 000000000..698fc572e --- /dev/null +++ b/website/src/components/guides/axes/scales.js @@ -0,0 +1,32 @@ +/* + * This file is part of the nivo project. + * + * Copyright 2016-present, Raphaël Benitte. + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +import { scaleLinear, scalePoint, scaleTime } from 'd3-scale' + +export const linearXScale = scaleLinear() + .range([0, 280]) + .domain([0, 80]) +linearXScale.type = 'linear' + +export const linearYScale = scaleLinear() + .range([160, 0]) + .domain([0, 35]) +linearYScale.type = 'linear' + +export const pointXScale = scalePoint() + .range([0, 280]) + .domain(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']) +pointXScale.type = 'point' + +const timeScaleStart = new Date(2019, 0, 1, 0, 0, 0, 0) +const timeScaleEnd = new Date(2020, 0, 1, 0, 0, 0, 0) + +export const timeXScale = scaleTime() + .range([0, 280]) + .domain([timeScaleStart, timeScaleEnd]) +timeXScale.type = 'time' diff --git a/website/src/lib/componentProperties.js b/website/src/lib/componentProperties.js index 1572cd0d1..c390cd2c3 100644 --- a/website/src/lib/componentProperties.js +++ b/website/src/lib/componentProperties.js @@ -162,6 +162,12 @@ export const axesProperties = [ max: 90, }, }, + { + key: `legend`, + description: `${axisKey} axis legend.`, + type: '{number}', + controlType: 'text', + }, { key: `legendOffset`, description: `${axisKey} axis legend offset from axis.`, diff --git a/website/src/styles/controls.css b/website/src/styles/controls.css index 8ddff046f..260a84b69 100644 --- a/website/src/styles/controls.css +++ b/website/src/styles/controls.css @@ -213,3 +213,13 @@ width: 8px; height: 8px; } + +.control-text { + font-size: 14px; + color: inherit; + border-radius: 2px; + border: 1px solid #ccc; + padding: 7px 10px; + width: 100%; + display: block; +}