Skip to content

Commit 4ae6591

Browse files
Raphaël BenitteRaphaël Benitte
Raphaël Benitte
authored and
Raphaël Benitte
committed
feat(scatterplot): improve scatterplot
1 parent 36cd9c7 commit 4ae6591

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4076
-3430
lines changed

.editorconfig

+5
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,9 @@ max_line_length = 100
66
[*.ts]
77
indent_style = space
88
indent_size = 4
9+
max_line_length = 100
10+
11+
[*.tsx]
12+
indent_style = space
13+
indent_size = 4
914
max_line_length = 100

.storybook/config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ function loadStories() {
5050
require('../packages/pie/stories/pie.stories')
5151
require('../packages/radar/stories/radar.stories')
5252
require('../packages/sankey/stories/sankey.stories')
53-
require('../packages/scatterplot/stories/scatterplot.stories')
53+
require('../packages/scatterplot/stories/ScatterPlot.stories')
54+
require('../packages/scatterplot/stories/ScatterPlotCanvas.stories')
5455
require('../packages/stream/stories/stream.stories')
5556
require('../packages/sunburst/stories/sunburst.stories')
5657
require('../packages/treemap/stories/treemap.stories')

Makefile

+10-3
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ init: ##@0 global cleanup/install/bootstrap
5454

5555
fmt: ##@0 global format code using prettier (js, css, md)
5656
@./node_modules/.bin/prettier --color --write \
57-
"packages/*/{src,stories,tests}/**/*.js" \
57+
"packages/*/{src,stories,tests}/**/*.{js,ts}" \
58+
"packages/*/index.d.ts" \
5859
"packages/*/README.md" \
5960
"website/src/**/*.{js,css}" \
6061
"examples/*/src/**/*.{js,ts,tsx,css}" \
@@ -63,10 +64,10 @@ fmt: ##@0 global format code using prettier (js, css, md)
6364
fmt-check: ##@0 global check if files were all formatted using prettier
6465
@echo "${YELLOW}Checking formatting${RESET}"
6566
@./node_modules/.bin/prettier --color --list-different \
66-
"packages/*/{src,stories,tests}/**/*.js" \
67+
"packages/*/{src,stories,tests}/**/*.{js,ts}" \
68+
"packages/*/index.d.ts" \
6769
"packages/*/README.md" \
6870
"website/src/**/*.{js,css}" \
69-
"storybook/stories/**/*.{js,css}" \
7071
"examples/*/src/**/*.{js,ts,tsx,css}" \
7172
"README.md"
7273

@@ -120,11 +121,17 @@ package-tslint-%: ##@1 packages run tslint on package
120121
packages-tslint: ##@1 packages run tslint on all packages
121122
@echo "${YELLOW}Running tslint on all packages${RESET}"
122123
@./node_modules/.bin/tslint \
124+
./packages/axes/index.d.ts \
123125
./packages/bar/index.d.ts \
124126
./packages/calendar/index.d.ts \
125127
./packages/core/index.d.ts \
126128
./packages/heatmap/index.d.ts \
129+
./packages/legends/index.d.ts \
130+
./packages/line/index.d.ts \
127131
./packages/pie/index.d.ts \
132+
./packages/sankey/index.d.ts \
133+
./packages/scales/index.d.ts \
134+
./packages/scatterplot/index.d.ts \
128135
./packages/waffle/index.d.ts
129136

130137
package-test-%: ##@1 packages run tests for a package

examples/typescript/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6-
"@nivo/line": "^0.50.0",
6+
"@nivo/line": "0.50.0",
7+
"@nivo/scatterplot": "0.50.0",
78
"react": "^16.5.2",
89
"react-dom": "^16.5.2",
910
"react-scripts-ts": "3.1.0"

examples/typescript/src/App.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as React from 'react'
22
import Line from './components/Line'
3+
import ScatterPlot from './components/ScatterPlot'
34
import './App.css'
45

56
export default class App extends React.Component {
@@ -9,6 +10,7 @@ export default class App extends React.Component {
910
<header className="App-header">
1011
<h1 className="App-title">nivo typescript example</h1>
1112
</header>
13+
<ScatterPlot />
1214
<Line />
1315
</div>
1416
)

examples/typescript/src/components/Line.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export default class Line extends React.Component {
6868
legend: 'whatever for x',
6969
legendOffset: -40,
7070
legendPosition: 'middle',
71+
format: (v: number) => `${v}`,
7172
}}
7273
enableGridX={true}
7374
enableGridY={true}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import * as React from 'react'
2+
import { ResponsiveScatterPlot, ScatterPlotDatum } from '@nivo/scatterplot'
3+
4+
export default class ScatterPlot extends React.Component {
5+
public render() {
6+
return (
7+
<div
8+
style={{
9+
height: 400,
10+
}}
11+
>
12+
<ResponsiveScatterPlot
13+
data={[
14+
{
15+
id: 'group A',
16+
data: [{ x: 0, y: 0 }, { x: 2, y: 7 }, { x: 7, y: 23 }],
17+
},
18+
{
19+
id: 'group B',
20+
data: [{ x: 3, y: 13 }, { x: 5, y: 7 }, { x: 6, y: 19 }],
21+
},
22+
]}
23+
xScale={{
24+
type: 'linear',
25+
min: 0,
26+
max: 'auto',
27+
}}
28+
yScale={{
29+
type: 'linear',
30+
min: 0,
31+
max: 30,
32+
}}
33+
theme={{
34+
grid: {
35+
line: {
36+
stroke: '#fdd',
37+
strokeDasharray: '8 4 1 4',
38+
},
39+
},
40+
}}
41+
margin={{
42+
top: 60,
43+
right: 100,
44+
bottom: 60,
45+
left: 100,
46+
}}
47+
enableGridX={true}
48+
enableGridY={true}
49+
symbolSize={12}
50+
symbolShape="circle"
51+
axisLeft={{
52+
tickSize: 5,
53+
tickPadding: 5,
54+
tickRotation: 0,
55+
legend: 'size',
56+
legendPosition: 'middle',
57+
legendOffset: -60,
58+
format: (v: number) => `${v}x`,
59+
}}
60+
legends={[
61+
{
62+
anchor: 'bottom-right',
63+
direction: 'column',
64+
translateX: -20,
65+
translateY: -20,
66+
itemWidth: 80,
67+
itemHeight: 24,
68+
itemsSpacing: 5,
69+
itemBackground: '#fff',
70+
itemTextColor: '#999',
71+
symbolSize: 12,
72+
symbolShape: 'circle',
73+
effects: [
74+
{
75+
on: 'hover',
76+
style: {
77+
itemTextColor: '#000',
78+
},
79+
},
80+
],
81+
},
82+
]}
83+
markers={[
84+
{
85+
axis: 'x',
86+
value: 4,
87+
legend: 'X marker a 4',
88+
lineStyle: {
89+
stroke: 'blue',
90+
},
91+
textStyle: {
92+
fill: 'blue',
93+
},
94+
},
95+
{
96+
axis: 'y',
97+
value: 20,
98+
legend: 'Y marker at 20',
99+
lineStyle: {
100+
stroke: 'red',
101+
},
102+
textStyle: {
103+
fill: 'red',
104+
},
105+
},
106+
]}
107+
isInteractive={true}
108+
animate={false}
109+
motionStiffness={150}
110+
motionDamping={15}
111+
onClick={(data: ScatterPlotDatum) => {
112+
console.log(data)
113+
}}
114+
/>
115+
</div>
116+
)
117+
}
118+
}

examples/typescript/yarn.lock

+13-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
lodash "^4.17.4"
5252
recompose "^0.26.0"
5353

54-
"@nivo/line@^0.50.0":
54+
5555
version "0.50.0"
5656
resolved "https://registry.yarnpkg.com/@nivo/line/-/line-0.50.0.tgz#61d4bfa2d840acc60c1e82e33bbd148817c19731"
5757
dependencies:
@@ -74,6 +74,18 @@
7474
d3-time-format "^2.1.3"
7575
lodash "^4.17.4"
7676

77+
78+
version "0.50.0"
79+
resolved "https://registry.yarnpkg.com/@nivo/scatterplot/-/scatterplot-0.50.0.tgz#f77201c4733b0c5068c163233be82c75ded14d2c"
80+
dependencies:
81+
"@nivo/core" "0.50.0"
82+
"@nivo/legends" "0.50.0"
83+
d3-scale "^2.1.2"
84+
d3-shape "^1.2.2"
85+
lodash "^4.17.4"
86+
react-motion "^0.5.2"
87+
recompose "^0.26.0"
88+
7789
"@types/jest@^23.3.5":
7890
version "23.3.5"
7991
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.5.tgz#870a1434208b60603745bfd214fc3fc675142364"

packages/axes/index.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ declare module '@nivo/axes' {
77
tickSize?: number
88
tickPadding?: number
99
tickRotation?: number
10-
tickFormat?: any // PropTypes.oneOfType([PropTypes.func, PropTypes.string])
10+
format?: any // PropTypes.oneOfType([PropTypes.func, PropTypes.string])
1111
renderTick?: (data: any) => React.ReactNode
1212
legend?: React.ReactNode
1313
legendPosition?: 'start' | 'middle' | 'end'

packages/axes/src/canvas.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* file that was distributed with this source code.
88
*/
99
import { degreesToRadians } from './utils'
10-
import { computeCartesianTicks } from './compute'
10+
import { computeCartesianTicks, getFormatter } from './compute'
1111

1212
export const renderAxisToCanvas = (
1313
ctx,
@@ -23,7 +23,7 @@ export const renderAxisToCanvas = (
2323
tickSize = 5,
2424
tickPadding = 5,
2525
tickRotation = 0,
26-
tickValueFormat,
26+
format,
2727

2828
// @todo implement legend support
2929
// legend,
@@ -68,7 +68,7 @@ export const renderAxisToCanvas = (
6868
ctx.lineTo(tick.x + tick.lineX, tick.y + tick.lineY)
6969
ctx.stroke()
7070

71-
const value = tickValueFormat !== undefined ? tickValueFormat(tick.value) : tick.value
71+
const value = format !== undefined ? format(tick.value) : tick.value
7272

7373
ctx.save()
7474
ctx.translate(tick.x + tick.textX, tick.y + tick.textY)
@@ -108,13 +108,16 @@ export const renderAxesToCanvas = (
108108

109109
const isXAxis = position === 'top' || position === 'bottom'
110110
const ticksPosition = position === 'top' || position === 'left' ? 'before' : 'after'
111+
const scale = isXAxis ? xScale : yScale
112+
const format = getFormatter(axis.format, scale)
111113

112114
renderAxisToCanvas(ctx, {
113115
...axis,
114116
axis: isXAxis ? 'x' : 'y',
115117
x: position === 'right' ? width : 0,
116118
y: position === 'bottom' ? height : 0,
117-
scale: isXAxis ? xScale : yScale,
119+
scale,
120+
format,
118121
length: isXAxis ? width : height,
119122
ticksPosition,
120123
theme,

packages/axes/src/components/Axis.js

+7-17
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@
88
*/
99
import React, { Component, Fragment } from 'react'
1010
import PropTypes from 'prop-types'
11-
import isFunction from 'lodash/isFunction'
12-
import { format as d3Format } from 'd3-format'
13-
import { timeFormat } from 'd3-time-format'
1411
import compose from 'recompose/compose'
1512
import withPropsOnChange from 'recompose/withPropsOnChange'
1613
import pure from 'recompose/pure'
1714
import setDisplayName from 'recompose/setDisplayName'
1815
import { Motion, TransitionMotion, spring } from 'react-motion'
19-
import { computeCartesianTicks } from '../compute'
2016
import { withMotion, motionPropTypes, axisThemePropType } from '@nivo/core'
17+
import { computeCartesianTicks, getFormatter } from '../compute'
2118
import AxisTick from './AxisTick'
2219

2320
const willEnter = () => ({
@@ -91,7 +88,7 @@ class Axis extends Component {
9188
tickSize,
9289
tickPadding,
9390
tickRotation,
94-
tickValueFormat,
91+
format,
9592
renderTick,
9693
legend,
9794
legendPosition,
@@ -165,7 +162,7 @@ class Axis extends Component {
165162
{ticks.map((tick, tickIndex) =>
166163
renderTick({
167164
tickIndex,
168-
format: tickValueFormat,
165+
format,
169166
rotate: tickRotation,
170167
textBaseline,
171168
textAnchor: textAlign,
@@ -214,7 +211,7 @@ class Axis extends Component {
214211
{interpolatedStyles.map(({ style, data: tick }, tickIndex) =>
215212
renderTick({
216213
tickIndex,
217-
format: tickValueFormat,
214+
format,
218215
textBaseline,
219216
textAnchor: textAlign,
220217
theme,
@@ -246,16 +243,9 @@ class Axis extends Component {
246243

247244
const enhance = compose(
248245
withMotion(),
249-
withPropsOnChange(['format', 'scale'], ({ format, scale }) => {
250-
if (!format || isFunction(format)) {
251-
return { format }
252-
} else if (scale.type === 'time') {
253-
const f = timeFormat(format)
254-
return { format: d => f(new Date(d)) }
255-
} else {
256-
return { format: d3Format(format) }
257-
}
258-
}),
246+
withPropsOnChange(['format', 'scale'], ({ format, scale }) => ({
247+
format: getFormatter(format, scale),
248+
})),
259249
pure
260250
)
261251

packages/axes/src/compute.js

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
*/
99
import isNumber from 'lodash/isNumber'
1010
import isArray from 'lodash/isArray'
11+
import isFunction from 'lodash/isFunction'
12+
import { timeFormat } from 'd3-time-format'
13+
import { format as d3Format } from 'd3-format'
1114
import { textPropsByEngine } from '@nivo/core'
1215

1316
export const centerScale = scale => {
@@ -107,3 +110,14 @@ export const computeCartesianTicks = ({
107110
textBaseline,
108111
}
109112
}
113+
114+
export const getFormatter = (format, scale) => {
115+
if (!format || isFunction(format)) return format
116+
117+
if (scale.type === 'time') {
118+
const f = timeFormat(format)
119+
return d => f(new Date(d))
120+
}
121+
122+
return d3Format(format)
123+
}

packages/axes/src/props.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const axisPropTypes = {
1919
tickSize: PropTypes.number,
2020
tickPadding: PropTypes.number,
2121
tickRotation: PropTypes.number,
22-
tickFormat: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
22+
format: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
2323
renderTick: PropTypes.func,
2424
legend: PropTypes.node,
2525
legendPosition: PropTypes.oneOf(['start', 'middle', 'end']),

0 commit comments

Comments
 (0)