diff --git a/packages/waffle/src/Waffle.js b/packages/waffle/src/Waffle.js index fa5126245..6e2f4031e 100644 --- a/packages/waffle/src/Waffle.js +++ b/packages/waffle/src/Waffle.js @@ -49,6 +49,8 @@ export class Waffle extends Component { render() { const { + hiddenIds, + // dimensions margin, width, @@ -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 ( @@ -148,7 +151,7 @@ export class Waffle extends Component { ) } else { - const computedCells = applyDataToGrid(cells, computedData) + const computedCells = applyDataToGrid(cells, computedData, hiddenIds) cellsRender = ( diff --git a/packages/waffle/src/enhance.js b/packages/waffle/src/enhance.js index c7bc08d89..f65475700 100644 --- a/packages/waffle/src/enhance.js +++ b/packages/waffle/src/enhance.js @@ -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, diff --git a/packages/waffle/src/props.js b/packages/waffle/src/props.js index 4e8e39428..163588dd5 100644 --- a/packages/waffle/src/props.js +++ b/packages/waffle/src/props.js @@ -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, @@ -69,6 +71,8 @@ export const WaffleCanvasPropTypes = { } const commonDefaultProps = { + hiddenIds: [], + // layout fillDirection: 'bottom', padding: 1, @@ -85,9 +89,6 @@ const commonDefaultProps = { // interactivity isInteractive: true, onClick: noop, - - // stack tooltip - enableStackTooltip: true, } export const WaffleDefaultProps = { diff --git a/packages/waffle/stories/waffle.stories.js b/packages/waffle/stories/waffle.stories.js index 2c90e0a37..2c196270d 100644 --- a/packages/waffle/stories/waffle.stories.js +++ b/packages/waffle/stories/waffle.stories.js @@ -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' @@ -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 ( + + ) + } +} + +stories.add('legend toggle', withInfo()(() => )) diff --git a/packages/waffle/tests/Waffle.test.js b/packages/waffle/tests/Waffle.test.js index 86bcb466e..5f8f5b7aa 100644 --- a/packages/waffle/tests/Waffle.test.js +++ b/packages/waffle/tests/Waffle.test.js @@ -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( @@ -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( - - ) +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( + + ) - 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( + + ) + + 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) }) diff --git a/website/src/components/charts/waffle/props.js b/website/src/components/charts/waffle/props.js index 3729dc573..318c54a15 100644 --- a/website/src/components/charts/waffle/props.js +++ b/website/src/components/charts/waffle/props.js @@ -46,6 +46,21 @@ export default [ type: '{Array}', required: true, }, + { + key: 'hiddenIds', + scopes: '*', + type: 'Array<{string | number}>', + description: ( +
+ 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. +
+ ), + required: false, + default: defaults.hiddenIds, + }, { key: 'rows', scopes: '*',