Skip to content

Commit

Permalink
feat(waffle): add ability to toggle datum by id
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphaël Benitte committed Aug 26, 2018
1 parent 87e3d9e commit 7f411da
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 66 deletions.
7 changes: 5 additions & 2 deletions packages/waffle/src/Waffle.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ export class Waffle extends Component {

render() {
const {
hiddenIds,

// dimensions
margin,
width,
Expand Down Expand Up @@ -119,7 +121,8 @@ export class Waffle extends Component {
...s.data,
startAt: Math.round(s.style.startAt),
endAt: Math.round(s.style.endAt),
}))
})),
hiddenIds
)

return (
Expand Down Expand Up @@ -148,7 +151,7 @@ export class Waffle extends Component {
</TransitionMotion>
)
} else {
const computedCells = applyDataToGrid(cells, computedData)
const computedCells = applyDataToGrid(cells, computedData, hiddenIds)

cellsRender = (
<Fragment>
Expand Down
43 changes: 28 additions & 15 deletions packages/waffle/src/enhance.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,38 @@ const commonEnhancers = [
return computeGrid(width, height, rows, columns, fillDirection, padding)
}
),
withPropsOnChange(['data', 'unit', 'getColor'], ({ data, unit, getColor }) => {
let currentPosition = 0
withPropsOnChange(
['data', 'unit', 'getColor', 'hiddenIds'],
({ data, unit, getColor, hiddenIds }) => {
let currentPosition = 0

return {
computedData: data.map((datum, groupIndex) => {
if (!hiddenIds.includes(datum.id)) {
const enhancedDatum = {
...datum,
groupIndex,
startAt: currentPosition,
endAt: currentPosition + Math.round(datum.value / unit),
color: getColor(datum),
}

return {
computedData: data.map((datum, groupIndex) => {
const enhancedDatum = {
...datum,
groupIndex,
startAt: currentPosition,
endAt: currentPosition + Math.round(datum.value / unit),
color: getColor(datum),
}
currentPosition = enhancedDatum.endAt

currentPosition = enhancedDatum.endAt
return enhancedDatum
}

return enhancedDatum
}),
return {
...datum,
groupIndex,
startAt: currentPosition,
endAt: currentPosition,
color: getColor(datum),
}
}),
}
}
}),
),
withPropsOnChange(['computedData'], ({ computedData }) => ({
legendData: computedData.map(datum => ({
id: datum.id,
Expand Down
7 changes: 4 additions & 3 deletions packages/waffle/src/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const commonPropTypes = {
value: PropTypes.number.isRequired,
})
).isRequired,
hiddenIds: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))
.isRequired,

// layout
rows: PropTypes.number.isRequired,
Expand Down Expand Up @@ -69,6 +71,8 @@ export const WaffleCanvasPropTypes = {
}

const commonDefaultProps = {
hiddenIds: [],

// layout
fillDirection: 'bottom',
padding: 1,
Expand All @@ -85,9 +89,6 @@ const commonDefaultProps = {
// interactivity
isInteractive: true,
onClick: noop,

// stack tooltip
enableStackTooltip: true,
}

export const WaffleDefaultProps = {
Expand Down
57 changes: 56 additions & 1 deletion packages/waffle/stories/waffle.stories.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, { Component } from 'react'
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import { patternDotsDef, patternLinesDef } from '@nivo/core'
Expand Down Expand Up @@ -129,3 +129,58 @@ stories.add(
/>
))
)

class WaffleLegendToggle extends Component {
state = {
hiddenIds: [],
}

toggle = d => {
const { hiddenIds } = this.state
if (this.state.hiddenIds.includes(d.id)) {
this.setState({
hiddenIds: hiddenIds.filter(id => id !== d.id),
})
} else {
this.setState({
hiddenIds: [...hiddenIds, d.id],
})
}
}

render() {
const { hiddenIds } = this.state

return (
<Waffle
{...commonProps}
hiddenIds={hiddenIds}
margin={{ left: 110 }}
legends={[
{
anchor: 'left',
direction: 'column',
itemsSpacing: 4,
itemWidth: 100,
itemHeight: 20,
symbolSize: 20,
itemTextColor: '#555',
translateX: -110,
onClick: this.toggle,
effects: [
{
on: 'hover',
style: {
itemTextColor: '#000',
itemBackground: '#eee',
},
},
],
},
]}
/>
)
}
}

stories.add('legend toggle', withInfo()(() => <WaffleLegendToggle />))
125 changes: 80 additions & 45 deletions packages/waffle/tests/Waffle.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import renderer from 'react-test-renderer'
import { mount } from 'enzyme'
import { LegendSvg, LegendSvgItem } from '@nivo/legends'
import Waffle from '../src/Waffle'
import WaffleCell from '../src/WaffleCell'

it('should render a basic waffle chart in SVG', () => {
const component = renderer.create(
Expand Down Expand Up @@ -40,52 +41,86 @@ for (const fillMode of fillModes) {
})
}

describe('legends', () => {
it('should support legends', () => {
const data = [
{ id: 'one', label: 'one', value: 10 },
{ id: 'two', label: 'two', value: 20 },
{ id: 'tree', label: 'tree', value: 30 },
]
const legends = [
{
anchor: 'top-left',
direction: 'column',
itemWidth: 100,
itemHeight: 20,
},
]
const wrapper = mount(
<Waffle
width={400}
height={400}
rows={10}
columns={10}
total={100}
colors={['red', 'green', 'blue']}
data={data}
legends={legends}
/>
)
it('should support legends', () => {
const data = [
{ id: 'one', label: 'one', value: 10 },
{ id: 'two', label: 'two', value: 20 },
{ id: 'tree', label: 'tree', value: 30 },
]
const legends = [
{
anchor: 'top-left',
direction: 'column',
itemWidth: 100,
itemHeight: 20,
},
]
const wrapper = mount(
<Waffle
width={400}
height={400}
rows={10}
columns={10}
total={100}
colors={['red', 'green', 'blue']}
data={data}
legends={legends}
/>
)

expect(wrapper.find(LegendSvg)).toHaveLength(1)
expect(wrapper.find(LegendSvg)).toHaveLength(1)

const legendItems = wrapper.find(LegendSvgItem)
expect(legendItems).toHaveLength(3)
expect(legendItems.at(0).prop('data')).toEqual({
id: 'one',
label: 'one',
color: 'red',
})
expect(legendItems.at(1).prop('data')).toEqual({
id: 'two',
label: 'two',
color: 'green',
})
expect(legendItems.at(2).prop('data')).toEqual({
id: 'tree',
label: 'tree',
color: 'blue',
})
const legendItems = wrapper.find(LegendSvgItem)
expect(legendItems).toHaveLength(3)
expect(legendItems.at(0).prop('data')).toEqual({
id: 'one',
label: 'one',
color: 'red',
})
expect(legendItems.at(1).prop('data')).toEqual({
id: 'two',
label: 'two',
color: 'green',
})
expect(legendItems.at(2).prop('data')).toEqual({
id: 'tree',
label: 'tree',
color: 'blue',
})
})

it('should allow to hide specific ids', () => {
const data = [{ id: 'one', label: 'one', value: 10 }, { id: 'two', label: 'two', value: 20 }]
const legends = [
{
anchor: 'top-left',
direction: 'column',
itemWidth: 100,
itemHeight: 20,
},
]
const wrapper = mount(
<Waffle
width={400}
height={400}
rows={10}
columns={10}
total={100}
colors={['red', 'green']}
data={data}
hiddenIds={['one']}
legends={legends}
/>
)

const oneCells = wrapper.findWhere(
n => n.type() === WaffleCell && n.prop('data') !== undefined && n.prop('data').id === 'one'
)
const twoCells = wrapper.findWhere(
n => n.type() === WaffleCell && n.prop('data') !== undefined && n.prop('data').id === 'two'
)
expect(oneCells).toHaveLength(0)
expect(twoCells.length).toBeGreaterThan(0)

expect(wrapper.find(LegendSvgItem)).toHaveLength(2)
})
15 changes: 15 additions & 0 deletions website/src/components/charts/waffle/props.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ export default [
type: '{Array<Object>}',
required: true,
},
{
key: 'hiddenIds',
scopes: '*',
type: 'Array<{string | number}>',
description: (
<div>
Hide parts of the data by id, this can be used to implement toggle. Note that the
datum will still be visible in legends, if you want to completely remove a datum
from the data set, you'll have to filter the data before passing it to the
component.
</div>
),
required: false,
default: defaults.hiddenIds,
},
{
key: 'rows',
scopes: '*',
Expand Down

0 comments on commit 7f411da

Please sign in to comment.