Skip to content

Commit

Permalink
feat(axes): improve tickValues support
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Aug 30, 2018
1 parent a88e50f commit 58aeaab
Show file tree
Hide file tree
Showing 13 changed files with 445 additions and 34 deletions.
3 changes: 1 addition & 2 deletions packages/bar/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 12 additions & 13 deletions packages/core/src/components/axes/Axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -60,7 +64,6 @@ const Axis = ({

// ticks
tickValues,
tickCount,
tickSize,
tickPadding,
tickRotation,
Expand Down Expand Up @@ -88,7 +91,6 @@ const Axis = ({
scale,
position: _position,
tickValues,
tickCount,
tickSize,
tickPadding,
tickRotation,
Expand Down Expand Up @@ -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,
}
Expand Down
29 changes: 15 additions & 14 deletions packages/core/src/lib/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down Expand Up @@ -51,16 +53,15 @@ const getScaleValues = (scale, tickCount) => {
*/

/**
* @param {number} width
* @param {number} height
* @param {string} _position
* @param {Object} scale
* @param {Array.<string|number>} [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.<string|number>} [_tickValues]
* @param {number} [tickSize=5]
* @param {number} [tickPadding=5]
* @param {number} [tickRotation=0]
* @parem {string} [engine='svg']
*
* @return {{ x: number, y: number, ticks: Array.<AxisTick>, textAlign: string, textBaseline: string }}
*/
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion packages/core/tests/lib/cartesian/axes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('computeAxisTicks()', () => {
scale: linearScale,
width,
height,
tickCount: 1,
tickValues: 1,
position: 'left',
})
expect(axis.ticks.length).toBe(2)
Expand Down
8 changes: 8 additions & 0 deletions website/src/SiteMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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',
Expand Down
14 changes: 10 additions & 4 deletions website/src/components/controls/TextControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@ export default class TextControl extends Component {

return (
<div className="chart-controls_item">
<input id={id} type="text" value={value} onChange={onChange} />
<label htmlFor={id} />
&nbsp;
<label htmlFor={id}>{label}</label>
<label className="control_label" htmlFor={id}>
{label}
</label>
<input
id={id}
type="text"
className="control-text"
value={value}
onChange={onChange}
/>
<div className="control-help">{help}</div>
</div>
)
Expand Down
48 changes: 48 additions & 0 deletions website/src/components/guides/axes/Axes.js
Original file line number Diff line number Diff line change
@@ -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 (
<div className="inner-content">
<Helmet title="Gradients" />
<div className="page_content">
<div className="guide__header">
<h1 className="page_header">Axes</h1>
</div>
</div>
<div className="guide__description text-content">
<h2>Using axes in nivo components</h2>
<p>
Axes are built on top of{' '}
<a
href="https://github.com/d3/d3-scale"
target="_blank"
rel="noopener noreferrer"
>
d3 scales
</a>
. A lot of nivo components make use of it (<Link to="/bar">Bar</Link>,{' '}
<Link to="/line">Line</Link>, <Link to="/scatterplot">ScatterPlot</Link>
…).
</p>
</div>
<AxesPosition />
<AxesTicks />
<AxesLegend />
</div>
)
}
}
83 changes: 83 additions & 0 deletions website/src/components/guides/axes/AxesLegend.js
Original file line number Diff line number Diff line change
@@ -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 (
<Fragment>
<div className="guide__description text-content">
<h2 id="legend">Axis legend</h2>
<p>
You can optionally add a legend to an axis by setting the value of the{' '}
<code>legend</code> property.
</p>
<h3 id="legend-position">Legend position</h3>
<p>
Legend position is controlled by two properties, <code>legendPosition</code>{' '}
and <code>legendOffset</code>.<code>legendPosition</code> must be one of:{' '}
<code>start</code>, <code>center</code> or <code>end</code>,{' '}
<code>legendOffset</code> will affect y position for <strong>top</strong>{' '}
and <strong>bottom</strong> axes and x position for <strong>left</strong>{' '}
and <strong>right</strong> axes.
</p>
</div>
<div className="banner">
<div
className="guide__illustrations"
style={{ justifyContent: 'center', alignItems: 'center' }}
>
<svg role="img" width={380} height={180}>
{axisPositions.map((position, i) => (
<g key={position} transform={`translate(50,${i * 70 + 40})`}>
<Axis
scale={linearXScale}
width={280}
height={0}
position="top"
theme={defaultTheme}
animate={false}
motionStiffness={0}
motionDamping={0}
legend={position}
legendPosition={position}
legendOffset={-32}
/>
</g>
))}
</svg>
<svg role="img" width={260} height={260}>
{axisPositions.map((position, i) => (
<g key={position} transform={`translate(${i * 90 + 50},50)`}>
<Axis
scale={linearYScale}
width={0}
height={160}
position="left"
theme={defaultTheme}
animate={false}
motionStiffness={0}
motionDamping={0}
legend={position}
legendPosition={position}
legendOffset={-32}
/>
</g>
))}
</svg>
</div>
</div>
</Fragment>
)
}
}
65 changes: 65 additions & 0 deletions website/src/components/guides/axes/AxesPosition.js
Original file line number Diff line number Diff line change
@@ -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 (
<Fragment>
<div className="guide__description text-content">
<h2 id="position">Axis position</h2>
<p>
Axis position is determined by the property you use{' '}
<strong>(top|right|bottom|left)Axis</strong>.
</p>
</div>
<div className="banner">
<div className="guide__illustrations">
<svg role="img" width={380} height={260}>
<g transform="translate(50,50)">
<Axes
xScale={linearXScale}
yScale={linearYScale}
width={280}
height={160}
theme={defaultTheme}
animate={false}
motionStiffness={0}
motionDamping={0}
top={{
legend: 'axisTop',
legendPosition: 'center',
legendOffset: -32,
}}
right={{
legend: 'axisRight',
legendPosition: 'center',
legendOffset: 42,
}}
bottom={{
legend: 'axisBottom',
legendPosition: 'center',
legendOffset: 38,
}}
left={{
legend: 'axisLeft',
legendPosition: 'center',
legendOffset: -36,
}}
/>
</g>
</svg>
</div>
</div>
</Fragment>
)
}
}
Loading

0 comments on commit 58aeaab

Please sign in to comment.