Skip to content

Commit

Permalink
feat(swarmplot): add time scale support (#1121)
Browse files Browse the repository at this point in the history
  • Loading branch information
SachinVarghese committed Sep 10, 2020
1 parent 54215e7 commit 9a19da6
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/scales/src/index.js
Expand Up @@ -17,6 +17,7 @@ export * from './linearScale'
export * from './logScale'
export * from './pointScale'
export * from './timeScale'
export * from './timeHelpers'

export const scalePropType = PropTypes.oneOfType([
PropTypes.shape(linearScalePropTypes),
Expand Down
28 changes: 22 additions & 6 deletions packages/swarmplot/src/compute.js
Expand Up @@ -12,7 +12,7 @@ import isNumber from 'lodash/isNumber'
import isPlainObject from 'lodash/isPlainObject'
import { scaleOrdinal, scaleLinear } from 'd3-scale'
import { forceSimulation, forceX, forceY, forceCollide } from 'd3-force'
import { computeScale } from '@nivo/scales'
import { computeScale, generateSeriesAxis, createDateNormalizer } from '@nivo/scales'

export const getSizeGenerator = size => {
if (typeof size === 'function') return size
Expand Down Expand Up @@ -46,10 +46,15 @@ export const getSizeGenerator = size => {

export const computeValueScale = ({ width, height, axis, getValue, scale, data }) => {
const values = data.map(getValue)
const min = Math.min(...values)
const max = Math.max(...values)

return computeScale({ ...scale, axis }, { [axis]: { min, max } }, width, height)
if (scale.type === 'time') {
const series = [{ data: values.map(p => ({ data: { [axis]: p } })) }]
const axes = generateSeriesAxis(series, axis, scale)
return computeScale({ ...scale, axis }, { [axis]: axes }, width, height)
} else {
const min = Math.min(...values)
const max = Math.max(...values)
return computeScale({ ...scale, axis }, { [axis]: { min, max } }, width, height)
}
}

export const computeOrdinalScale = ({ width, height, axis, groups, gap }) => {
Expand Down Expand Up @@ -90,6 +95,16 @@ export const computeForces = ({ axis, valueScale, ordinalScale, spacing, forceSt
return { x: xForce, y: yForce, collision: collisionForce }
}

export const getParsedValue = scaleSpec => {
if (scaleSpec.type === 'linear') {
return parseFloat
} else if (scaleSpec.type === 'time' && scaleSpec.format !== 'native') {
return createDateNormalizer(scaleSpec)
} else {
return x => x
}
}

export const computeNodes = ({
data,
getIdentity,
Expand All @@ -101,6 +116,7 @@ export const computeNodes = ({
getSize,
forces,
simulationIterations,
valueScaleConfig,
}) => {
const config = {
horizontal: ['x', 'y'],
Expand All @@ -110,7 +126,7 @@ export const computeNodes = ({
const simulatedNodes = data.map(d => ({
id: getIdentity(d),
group: getGroup(d),
value: getValue(d),
value: getParsedValue(valueScaleConfig)(getValue(d)),
size: getSize(d),
data: { ...d },
}))
Expand Down
1 change: 1 addition & 0 deletions packages/swarmplot/src/hooks.js
Expand Up @@ -134,6 +134,7 @@ export const useSwarmPlot = ({
getSize,
forces,
simulationIterations,
valueScaleConfig,
}),
[
data,
Expand Down
70 changes: 70 additions & 0 deletions packages/swarmplot/stories/SwarmPlot.stories.js
Expand Up @@ -74,3 +74,73 @@ stories.add('using annotations', () => (
]}
/>
))

const localeTimeFormat = value => new Date(value).toLocaleString()

stories.add('using time scale', () => (
<SwarmPlot
{...commonProps}
data={[
{
group: 'group A',
id: '21c4519f-713f-473b-8ce5-e4078d8822c9',
timestamp: '2020-09-04T23:10:13.002Z',
volume: 18,
},
{
group: 'group A',
id: '91c4519f-713f-473b-8ce5-e4078d882e77',
timestamp: '2020-09-05T15:10:13.002Z',
volume: 16,
},
{
group: 'group B',
id: '91c4519f-713f-473b-8ce5-e4078d882e67',
timestamp: '2020-09-05T05:10:13.002Z',
volume: 16,
},
{
group: 'group C',
id: '91c4519f-713f-473b-8ce5-e4078d882t67',
timestamp: '2020-09-05T05:10:13.002Z',
volume: 14,
},
]}
margin={{
top: 40,
right: 40,
bottom: 40,
left: 100,
}}
value="timestamp"
valueFormat={localeTimeFormat}
valueScale={{
type: 'time',
format: '%Y-%m-%dT%H:%M:%S.%LZ',
}}
size={{ key: 'volume', values: [4, 20], sizes: [6, 20] }}
axisTop={null}
axisRight={null}
axisBottom={{
format: localeTimeFormat,
orient: 'bottom',
tickSize: 10,
tickPadding: 5,
tickRotation: 0,
legend: 'timestamp',
legendPosition: 'middle',
legendOffset: 46,
tickValues: 'every 4 hours',
}}
axisLeft={{
orient: 'left',
tickSize: 10,
tickPadding: 5,
tickRotation: 0,
legend: 'groups',
legendPosition: 'middle',
legendOffset: -76,
}}
layout="horizontal"
/>
))

0 comments on commit 9a19da6

Please sign in to comment.