From 0b6cd941fe42f63e8d676a492efc03cb8cb1f97f Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Thu, 5 Nov 2020 22:29:21 -0600 Subject: [PATCH 01/18] feat(bullet): init package migration to typescript Close #1224 --- packages/bullet/package.json | 11 +- packages/bullet/src/{Bullet.js => Bullet.tsx} | 27 +-- .../src/{BulletItem.js => BulletItem.tsx} | 220 +++++++----------- .../{BulletMarkers.js => BulletMarkers.tsx} | 69 +++--- packages/bullet/src/BulletMarkersItem.js | 60 ----- packages/bullet/src/BulletMarkersItem.tsx | 35 +++ .../src/{BulletRects.js => BulletRects.tsx} | 70 ++---- packages/bullet/src/BulletRectsItem.js | 57 ----- packages/bullet/src/BulletRectsItem.tsx | 32 +++ packages/bullet/src/ResponsiveBullet.js | 19 -- packages/bullet/src/ResponsiveBullet.tsx | 13 ++ .../bullet/src/{compute.js => compute.ts} | 44 ++-- packages/bullet/src/enhance.js | 28 --- packages/bullet/src/enhance.ts | 19 ++ packages/bullet/src/index.js | 12 - packages/bullet/src/index.ts | 4 + packages/bullet/src/props.js | 103 -------- packages/bullet/src/props.ts | 29 +++ packages/bullet/src/types.ts | 184 +++++++++++++++ packages/bullet/tsconfig.json | 8 + tsconfig.monorepo.json | 3 +- yarn.lock | 26 +++ 22 files changed, 521 insertions(+), 552 deletions(-) rename packages/bullet/src/{Bullet.js => Bullet.tsx} (89%) rename packages/bullet/src/{BulletItem.js => BulletItem.tsx} (56%) rename packages/bullet/src/{BulletMarkers.js => BulletMarkers.tsx} (70%) delete mode 100644 packages/bullet/src/BulletMarkersItem.js create mode 100644 packages/bullet/src/BulletMarkersItem.tsx rename packages/bullet/src/{BulletRects.js => BulletRects.tsx} (68%) delete mode 100644 packages/bullet/src/BulletRectsItem.js create mode 100644 packages/bullet/src/BulletRectsItem.tsx delete mode 100644 packages/bullet/src/ResponsiveBullet.js create mode 100644 packages/bullet/src/ResponsiveBullet.tsx rename packages/bullet/src/{compute.js => compute.ts} (58%) delete mode 100644 packages/bullet/src/enhance.js create mode 100644 packages/bullet/src/enhance.ts delete mode 100644 packages/bullet/src/index.js create mode 100644 packages/bullet/src/index.ts delete mode 100644 packages/bullet/src/props.js create mode 100644 packages/bullet/src/props.ts create mode 100644 packages/bullet/src/types.ts create mode 100644 packages/bullet/tsconfig.json diff --git a/packages/bullet/package.json b/packages/bullet/package.json index a4bcf9bf7..1f8f0dbe0 100644 --- a/packages/bullet/package.json +++ b/packages/bullet/package.json @@ -16,16 +16,15 @@ ], "main": "./dist/nivo-bullet.cjs.js", "module": "./dist/nivo-bullet.es.js", + "typings": "./dist/types/index.d.ts", "files": [ "README.md", "LICENSE.md", - "index.d.ts", "dist/" ], "dependencies": { "@nivo/axes": "0.64.0", "@nivo/colors": "0.64.0", - "@nivo/core": "0.64.0", "@nivo/legends": "0.64.0", "@nivo/tooltip": "0.64.0", "d3-scale": "^3.0.0", @@ -33,8 +32,14 @@ "react-motion": "^0.5.2", "recompose": "^0.30.0" }, + "devDependencies": { + "@nivo/core": "*", + "@types/d3-scale": "^3.2.1", + "@types/react-motion": "^0.0.29", + "@types/recompose": "^0.30.7" + }, "peerDependencies": { - "prop-types": ">= 15.5.10 < 16.0.0", + "@nivo/core": "^0.64.0", "react": ">= 16.8.4 < 17.0.0" }, "publishConfig": { diff --git a/packages/bullet/src/Bullet.js b/packages/bullet/src/Bullet.tsx similarity index 89% rename from packages/bullet/src/Bullet.js rename to packages/bullet/src/Bullet.tsx index d7254e56e..a8f31ced2 100644 --- a/packages/bullet/src/Bullet.js +++ b/packages/bullet/src/Bullet.tsx @@ -1,21 +1,16 @@ -/* - * 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 { scaleLinear } from 'd3-scale' +// @ts-ignore import setDisplayName from 'recompose/setDisplayName' +// @ts-ignore import { Container, SvgWrapper } from '@nivo/core' -import { BulletPropTypes } from './props' +import { defaultProps } from './props' +import { BulletSvgProps, TooltipHandlers } from './types' import enhance from './enhance' import BulletItem from './BulletItem' -export class Bullet extends Component { - static propTypes = BulletPropTypes +export class Bullet extends Component { + static displayName = 'Bullet' render() { const { @@ -61,9 +56,9 @@ export class Bullet extends Component { onMarkerClick, role, - } = this.props + } = { height: 0, width: 0, ...defaultProps, ...this.props } - let itemHeight + let itemHeight: number if (layout === 'horizontal') { itemHeight = (height - spacing * (data.length - 1)) / data.length } else { @@ -73,7 +68,7 @@ export class Bullet extends Component { const markerHeight = itemHeight * markerSize const enhancedData = data.map(d => { - const all = [...d.ranges, ...d.measures, ...d.markers] + const all = [...d.ranges, ...d.measures, ...(d.markers ?? [])] const max = Math.max(...all) @@ -101,7 +96,7 @@ export class Bullet extends Component { motionStiffness={motionStiffness} motionDamping={motionDamping} > - {({ showTooltip, hideTooltip }) => ( + {({ showTooltip, hideTooltip }: TooltipHandlers) => ( { - const { theme } = this.props +import { BulletItemProps, ComputedRangeDatum, ComputedMarkersDatum, TooltipHandlers } from './types' + +type TooltipEvent = ( + showTooltip: TooltipHandlers['showTooltip'], + datum: Datum, + event: React.MouseEvent +) => void + +type MouseEventWithDatum = ( + datum: Datum, + event: React.MouseEvent +) => void + +class BulletItem extends Component { + handleRangeTooltip: TooltipEvent = ( + showTooltip, + range, + event + ) => { showTooltip( , event ) } - handleMeasureTooltip = (showTooltip, measure, event) => { - const { theme } = this.props + handleMeasureTooltip: TooltipEvent = ( + showTooltip, + measure, + event + ) => { showTooltip( {measure.v1}} enableChip={true} color={measure.color} - theme={theme} - //format={format} - //renderContent={typeof tooltip === 'function' ? tooltip.bind(null, { ...node }) : null} />, event ) } - handleMarkerTooltip = (showTooltip, marker, event) => { - const { theme } = this.props + handleMarkerTooltip: TooltipEvent = ( + showTooltip, + marker, + event + ) => { showTooltip( {marker.value}} enableChip={true} color={marker.color} - theme={theme} - //format={format} - //renderContent={typeof tooltip === 'function' ? tooltip.bind(null, { ...node }) : null} />, event ) } - handleRangeClick = (range, event) => { + handleRangeClick: MouseEventWithDatum = (range, event) => { const { id, onRangeClick } = this.props - onRangeClick({ id, ...range }, event) + onRangeClick?.({ id, ...range }, event) } - handleMeasureClick = (measure, event) => { + handleMeasureClick: MouseEventWithDatum = ( + measure, + event + ) => { const { id, onMeasureClick } = this.props - onMeasureClick({ id, ...measure }, event) + onMeasureClick?.({ id, ...measure }, event) } - handleMarkerClick = (marker, event) => { + handleMarkerClick: MouseEventWithDatum = ( + marker, + event + ) => { const { id, onMarkerClick } = this.props - onMarkerClick({ id, ...marker }, event) + onMarkerClick?.({ id, ...marker }, event) } render() { @@ -191,9 +132,9 @@ class BulletItem extends Component { showTooltip, hideTooltip, - animate, // eslint-disable-line react/prop-types - motionStiffness, // eslint-disable-line react/prop-types - motionDamping, // eslint-disable-line react/prop-types + animate, + motionStiffness, + motionDamping, } = this.props const motionProps = { @@ -247,6 +188,7 @@ class BulletItem extends Component { const axis = ( ({ + withPropsOnChange(['rangeColors', 'scale'], ({ rangeColors, scale }) => ({ rangeColorScale: getColorScale(rangeColors, scale, true), })), - withPropsOnChange(['ranges', 'rangeColorScale'], ({ ranges, rangeColorScale }) => ({ + withPropsOnChange(['ranges', 'rangeColorScale'], ({ ranges, rangeColorScale }) => ({ computedRanges: stackValues(ranges, rangeColorScale), })), - withPropsOnChange(['measureColors', 'scale'], ({ measureColors, scale }) => ({ + withPropsOnChange(['measureColors', 'scale'], ({ measureColors, scale }) => ({ measureColorScale: getColorScale(measureColors, scale), })), - withPropsOnChange(['measures', 'measureColorScale'], ({ measures, measureColorScale }) => ({ - computedMeasures: stackValues(measures, measureColorScale), - })), - withPropsOnChange(['markerColors', 'scale'], ({ markerColors, scale }) => ({ + withPropsOnChange( + ['measures', 'measureColorScale'], + ({ measures, measureColorScale }) => ({ + computedMeasures: stackValues(measures, measureColorScale), + }) + ), + withPropsOnChange(['markerColors', 'scale'], ({ markerColors, scale }) => ({ markerColorScale: getColorScale(markerColors, scale), })), - withPropsOnChange(['markers', 'markerColorScale'], ({ markers, markerColorScale }) => ({ - computedMarkers: markers.map((marker, index) => ({ - value: marker, - index, - color: markerColorScale(markerColorScale.type === 'sequential' ? marker : index), - })), - })), + withPropsOnChange( + ['markers', 'markerColorScale'], + ({ markers, markerColorScale }) => ({ + computedMarkers: markers.map((marker: number, index: number) => ({ + value: marker, + index, + color: markerColorScale(markerColorScale.type === 'sequential' ? marker : index), + })), + }) + ), pure -)(BulletItem) +)(BulletItem as any) as React.ComponentType< + Omit< + BulletItemProps, + | 'computedRanges' + | 'rangeColorScale' + | 'computedMeasures' + | 'measureColorScale' + | 'computedMarkers' + | 'markerColorScale' + > +> EnhancedBulletItem.displayName = 'BulletItem' diff --git a/packages/bullet/src/BulletMarkers.js b/packages/bullet/src/BulletMarkers.tsx similarity index 70% rename from packages/bullet/src/BulletMarkers.js rename to packages/bullet/src/BulletMarkers.tsx index 7998d086c..bc282252c 100644 --- a/packages/bullet/src/BulletMarkers.js +++ b/packages/bullet/src/BulletMarkers.tsx @@ -1,21 +1,26 @@ -/* - * 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 PropTypes from 'prop-types' +import React, { Component } from 'react' import { TransitionMotion, spring } from 'react-motion' -import { motionPropTypes } from '@nivo/core' +// @ts-ignore import { interpolateColor, getInterpolatedColor } from '@nivo/colors' import partial from 'lodash/partial' +import { BulletMarkersProps, ComputedMarkersDatum } from './types' -const getPositionGenerator = ({ layout, reverse, scale, height, markerSize }) => { +type MouseEventWithDatum = ( + datum: ComputedMarkersDatum, + event: React.MouseEvent +) => void + +type EventHandlers = Record<'onMouseEnter' | 'onMouseLeave' | 'onClick', MouseEventWithDatum> + +const getPositionGenerator = ({ + layout, + reverse, + scale, + height, + markerSize, +}: Pick) => { if (layout === 'horizontal') { - return marker => { + return (marker: ComputedMarkersDatum) => { const x = scale(marker.value) const y = height / 2 const rotation = reverse === true ? 180 : 0 @@ -24,7 +29,7 @@ const getPositionGenerator = ({ layout, reverse, scale, height, markerSize }) => } } - return marker => { + return (marker: ComputedMarkersDatum) => { const x = height / 2 const y = scale(marker.value) const rotation = reverse === true ? 270 : 90 @@ -33,36 +38,16 @@ const getPositionGenerator = ({ layout, reverse, scale, height, markerSize }) => } } -export default class BulletMarkers extends Component { - static propTypes = { - scale: PropTypes.func.isRequired, - layout: PropTypes.oneOf(['horizontal', 'vertical']).isRequired, - reverse: PropTypes.bool.isRequired, - markers: PropTypes.arrayOf( - PropTypes.shape({ - value: PropTypes.number.isRequired, - index: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, - }) - ).isRequired, - height: PropTypes.number.isRequired, - markerSize: PropTypes.number.isRequired, - component: PropTypes.func.isRequired, - onMouseEnter: PropTypes.func.isRequired, - onMouseLeave: PropTypes.func.isRequired, - onClick: PropTypes.func.isRequired, - ...motionPropTypes, - } - - handleMouseEnter = (data, event) => { +export default class BulletMarkers extends Component { + handleMouseEnter: MouseEventWithDatum = (data, event) => { this.props.onMouseEnter(data, event) } - handleMouseLeave = (data, event) => { + handleMouseLeave: MouseEventWithDatum = (data, event) => { this.props.onMouseLeave(data, event) } - handleClick = (data, event) => { + handleClick: MouseEventWithDatum = (data, event) => { this.props.onClick(data, event) } @@ -84,7 +69,7 @@ export default class BulletMarkers extends Component { if (animate !== true) { return ( - + <> {markers.map(marker => React.createElement(component, { key: marker.index, @@ -97,7 +82,7 @@ export default class BulletMarkers extends Component { onClick: partial(this.handleClick, marker), }) )} - + ) } @@ -125,7 +110,7 @@ export default class BulletMarkers extends Component { })} > {interpolatedStyles => ( - + <> {interpolatedStyles.map(({ key, style, data: marker }) => { const color = getInterpolatedColor(style) @@ -141,7 +126,7 @@ export default class BulletMarkers extends Component { onClick: partial(this.handleClick, marker), }) })} - + )} ) diff --git a/packages/bullet/src/BulletMarkersItem.js b/packages/bullet/src/BulletMarkersItem.js deleted file mode 100644 index b36ad3a5b..000000000 --- a/packages/bullet/src/BulletMarkersItem.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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, { PureComponent } from 'react' -import PropTypes from 'prop-types' - -export default class BulletMarkersItem extends PureComponent { - static propTypes = { - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - size: PropTypes.number.isRequired, - rotation: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, - data: PropTypes.shape({ - index: PropTypes.number.isRequired, - value: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, - }).isRequired, - onMouseEnter: PropTypes.func.isRequired, - onMouseMove: PropTypes.func.isRequired, - onMouseLeave: PropTypes.func.isRequired, - onClick: PropTypes.func.isRequired, - } - - render() { - const { - x, - y, - size, - rotation, - color, - onMouseEnter, - onMouseMove, - onMouseLeave, - onClick, - } = this.props - - return ( - - ) - } -} diff --git a/packages/bullet/src/BulletMarkersItem.tsx b/packages/bullet/src/BulletMarkersItem.tsx new file mode 100644 index 000000000..9a31c4222 --- /dev/null +++ b/packages/bullet/src/BulletMarkersItem.tsx @@ -0,0 +1,35 @@ +import React, { PureComponent } from 'react' +import { BulletMarkersItemProps } from './types' + +export default class BulletMarkersItem extends PureComponent { + render() { + const { + x, + y, + size, + rotation, + color, + onMouseEnter, + onMouseMove, + onMouseLeave, + onClick, + } = this.props + + return ( + + ) + } +} diff --git a/packages/bullet/src/BulletRects.js b/packages/bullet/src/BulletRects.tsx similarity index 68% rename from packages/bullet/src/BulletRects.js rename to packages/bullet/src/BulletRects.tsx index ccdda2687..284abcaaa 100644 --- a/packages/bullet/src/BulletRects.js +++ b/packages/bullet/src/BulletRects.tsx @@ -1,67 +1,31 @@ -/* - * 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 PropTypes from 'prop-types' import partial from 'lodash/partial' import { TransitionMotion, spring } from 'react-motion' import compose from 'recompose/compose' import withPropsOnChange from 'recompose/withPropsOnChange' import pure from 'recompose/pure' -import { motionPropTypes } from '@nivo/core' +// @ts-ignore import { interpolateColor, getInterpolatedColor } from '@nivo/colors' import { computeRects } from './compute' +import { BulletRectsProps, ComputedRangeDatum } from './types' -class BulletRects extends Component { - static propTypes = { - scale: PropTypes.func.isRequired, - data: PropTypes.arrayOf( - PropTypes.shape({ - v0: PropTypes.number.isRequired, - v1: PropTypes.number.isRequired, - }) - ).isRequired, - layout: PropTypes.oneOf(['horizontal', 'vertical']).isRequired, - reverse: PropTypes.bool.isRequired, - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - rects: PropTypes.arrayOf( - PropTypes.shape({ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - data: PropTypes.shape({ - index: PropTypes.number.isRequired, - v0: PropTypes.number.isRequired, - v1: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, - }).isRequired, - }) - ).isRequired, - component: PropTypes.func.isRequired, - onMouseEnter: PropTypes.func.isRequired, - onMouseLeave: PropTypes.func.isRequired, - onClick: PropTypes.func.isRequired, - ...motionPropTypes, - } +type MouseEventWithDatum = ( + datum: ComputedRangeDatum, + event: React.MouseEvent +) => void + +type EventHandlers = Record<'onMouseEnter' | 'onMouseLeave' | 'onClick', MouseEventWithDatum> - handleMouseEnter = (data, event) => { +class BulletRects extends Component { + handleMouseEnter: MouseEventWithDatum = (data, event) => { this.props.onMouseEnter(data, event) } - handleMouseLeave = (data, event) => { + handleMouseLeave: MouseEventWithDatum = (data, event) => { this.props.onMouseLeave(data, event) } - handleClick = (data, event) => { + handleClick: MouseEventWithDatum = (data, event) => { this.props.onClick(data, event) } @@ -142,7 +106,13 @@ class BulletRects extends Component { const EnhancedBulletRects = compose( withPropsOnChange( ['data', 'layout', 'reverse', 'scale', 'height'], - ({ data, layout, reverse, scale, height }) => ({ + ({ + data, + layout, + reverse, + scale, + height, + }: Pick) => ({ rects: computeRects({ data, layout, @@ -153,7 +123,7 @@ const EnhancedBulletRects = compose( }) ), pure -)(BulletRects) +)(BulletRects as any) as React.ComponentClass & EventHandlers> EnhancedBulletRects.displayName = 'BulletRects' diff --git a/packages/bullet/src/BulletRectsItem.js b/packages/bullet/src/BulletRectsItem.js deleted file mode 100644 index 9fe8a1094..000000000 --- a/packages/bullet/src/BulletRectsItem.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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, { PureComponent } from 'react' -import PropTypes from 'prop-types' - -export default class BulletRectsItem extends PureComponent { - static propTypes = { - index: PropTypes.number.isRequired, - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, - data: PropTypes.shape({ - v0: PropTypes.number.isRequired, - v1: PropTypes.number.isRequired, - }).isRequired, - onMouseEnter: PropTypes.func.isRequired, - onMouseMove: PropTypes.func.isRequired, - onMouseLeave: PropTypes.func.isRequired, - onClick: PropTypes.func.isRequired, - } - - render() { - const { - x, - y, - width, - height, - color, - onMouseEnter, - onMouseMove, - onMouseLeave, - onClick, - } = this.props - - return ( - - ) - } -} diff --git a/packages/bullet/src/BulletRectsItem.tsx b/packages/bullet/src/BulletRectsItem.tsx new file mode 100644 index 000000000..8a2091296 --- /dev/null +++ b/packages/bullet/src/BulletRectsItem.tsx @@ -0,0 +1,32 @@ +import React, { PureComponent } from 'react' +import { BulletRectsItemProps } from './types' + +export default class BulletRectsItem extends PureComponent { + render() { + const { + x, + y, + width, + height, + color, + onMouseEnter, + onMouseMove, + onMouseLeave, + onClick, + } = this.props + + return ( + + ) + } +} diff --git a/packages/bullet/src/ResponsiveBullet.js b/packages/bullet/src/ResponsiveBullet.js deleted file mode 100644 index ef06355af..000000000 --- a/packages/bullet/src/ResponsiveBullet.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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 from 'react' -import { ResponsiveWrapper } from '@nivo/core' -import Bullet from './Bullet' - -const ResponsiveBullet = props => ( - - {({ width, height }) => } - -) - -export default ResponsiveBullet diff --git a/packages/bullet/src/ResponsiveBullet.tsx b/packages/bullet/src/ResponsiveBullet.tsx new file mode 100644 index 000000000..7517d67cc --- /dev/null +++ b/packages/bullet/src/ResponsiveBullet.tsx @@ -0,0 +1,13 @@ +import React from 'react' +// @ts-ignore +import { ResponsiveWrapper, Dimensions } from '@nivo/core' +import { BulletSvgProps } from './types' +import Bullet from './Bullet' + +const ResponsiveBullet = (props: Omit) => ( + + {({ width, height }: Dimensions) => } + +) + +export default ResponsiveBullet diff --git a/packages/bullet/src/compute.js b/packages/bullet/src/compute.ts similarity index 58% rename from packages/bullet/src/compute.js rename to packages/bullet/src/compute.ts index 42311b133..e8179da90 100644 --- a/packages/bullet/src/compute.js +++ b/packages/bullet/src/compute.ts @@ -1,19 +1,21 @@ -/* - * 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 last from 'lodash/last' +import { BulletRectsProps, ComputedRangeDatum } from './types' +// @ts-ignore +import { getColorScale } from '@nivo/core' -export const stackValues = (values, colorScale, useAverage = false) => { +type ComputeDatum = BulletRectsProps['data'] extends Array ? U : never +type ComputeRect = Pick + +export const stackValues = ( + values: number[], + colorScale: ReturnType, + useAverage = false +) => { const normalized = [...values].filter(v => v !== 0).sort((a, b) => a - b) - return normalized.reduce((acc, v1, index) => { - const v0 = last(acc) !== undefined ? last(acc).v1 : 0 - let sequentialValue = useAverage === true ? v0 + (v1 - v0) / 2 : v1 + return normalized.reduce((acc, v1, index) => { + const v0 = last(acc)?.v1 ?? 0 + const sequentialValue = useAverage === true ? v0 + (v1 - v0) / 2 : v1 return [ ...acc, @@ -27,10 +29,10 @@ export const stackValues = (values, colorScale, useAverage = false) => { }, []) } -export const getComputeRect = ({ layout, reverse, scale, height }) => { +export const getComputeRect = ({ layout, reverse, scale, height }: ComputeRect) => { if (layout === 'horizontal') { if (reverse === true) { - return d => { + return (d: ComputeDatum) => { const x = scale(d.v1) const w = scale(d.v0) - x @@ -38,7 +40,7 @@ export const getComputeRect = ({ layout, reverse, scale, height }) => { } } - return d => { + return (d: ComputeDatum) => { const x = scale(d.v0) const w = scale(d.v1) - x @@ -47,7 +49,7 @@ export const getComputeRect = ({ layout, reverse, scale, height }) => { } if (reverse === true) { - return d => { + return (d: ComputeDatum) => { const y = scale(d.v0) const h = scale(d.v1) - y @@ -55,7 +57,7 @@ export const getComputeRect = ({ layout, reverse, scale, height }) => { } } - return d => { + return (d: ComputeDatum) => { const y = scale(d.v1) const h = scale(d.v0) - y @@ -63,7 +65,13 @@ export const getComputeRect = ({ layout, reverse, scale, height }) => { } } -export const computeRects = ({ data, layout, reverse, scale, height }) => { +export const computeRects = ({ + data, + layout, + reverse, + scale, + height, +}: Pick & ComputeRect) => { const computeRect = getComputeRect({ layout, reverse, diff --git a/packages/bullet/src/enhance.js b/packages/bullet/src/enhance.js deleted file mode 100644 index 2613ea41c..000000000 --- a/packages/bullet/src/enhance.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 compose from 'recompose/compose' -import defaultProps from 'recompose/defaultProps' -import pure from 'recompose/pure' -import { withDimensions, withTheme, withMotion } from '@nivo/core' -import * as props from './props' - -const commonEnhancers = [withDimensions(), withTheme()] - -export default Component => { - const implDefaultProps = props[`${Component.displayName}DefaultProps`] - - switch (Component.displayName) { - case 'Bullet': - return compose( - ...[defaultProps(implDefaultProps), ...commonEnhancers, withMotion(), pure] - )(Component) - } - - return Component -} diff --git a/packages/bullet/src/enhance.ts b/packages/bullet/src/enhance.ts new file mode 100644 index 000000000..6df12f7d3 --- /dev/null +++ b/packages/bullet/src/enhance.ts @@ -0,0 +1,19 @@ +import compose from 'recompose/compose' +import defaultProps from 'recompose/defaultProps' +import pure from 'recompose/pure' +// @ts-ignore +import { withDimensions, withTheme, withMotion } from '@nivo/core' +import { defaultProps as bulletDefaultProps } from './props' + +const commonEnhancers = [withDimensions(), withTheme()] + +export default (Component: React.ComponentType) => { + switch (Component.displayName) { + case 'Bullet': + return compose( + ...[defaultProps(bulletDefaultProps), ...commonEnhancers, withMotion(), pure] + )(Component) + } + + return Component +} diff --git a/packages/bullet/src/index.js b/packages/bullet/src/index.js deleted file mode 100644 index 6b738d672..000000000 --- a/packages/bullet/src/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/* - * 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. - */ -export { default as Bullet } from './Bullet' -export { default as BulletItem } from './BulletItem' -export { default as ResponsiveBullet } from './ResponsiveBullet' -export * from './props' diff --git a/packages/bullet/src/index.ts b/packages/bullet/src/index.ts new file mode 100644 index 000000000..bd646ed9b --- /dev/null +++ b/packages/bullet/src/index.ts @@ -0,0 +1,4 @@ +export { default as Bullet } from './Bullet' +export { default as BulletItem } from './BulletItem' +export { default as ResponsiveBullet } from './ResponsiveBullet' +export * from './props' diff --git a/packages/bullet/src/props.js b/packages/bullet/src/props.js deleted file mode 100644 index 7d4e806c7..000000000 --- a/packages/bullet/src/props.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 PropTypes from 'prop-types' -import { themePropType, noop } from '@nivo/core' -import BulletItem from './BulletItem' - -const commonPropTypes = { - data: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string.isRequired, - title: PropTypes.node, - ranges: PropTypes.arrayOf(PropTypes.number).isRequired, - measures: PropTypes.arrayOf(PropTypes.number).isRequired, - markers: PropTypes.arrayOf(PropTypes.number), - }) - ).isRequired, - layout: PropTypes.oneOf(['horizontal', 'vertical']).isRequired, - reverse: PropTypes.bool.isRequired, - spacing: PropTypes.number.isRequired, - - titlePosition: PropTypes.oneOf(['before', 'after']).isRequired, - titleAlign: PropTypes.oneOf(['start', 'middle', 'end']).isRequired, - titleOffsetX: PropTypes.number.isRequired, - titleOffsetY: PropTypes.number.isRequired, - titleRotation: PropTypes.number.isRequired, - - rangeColors: PropTypes.any.isRequired, - rangeBorderWidth: PropTypes.number.isRequired, - rangeBorderColor: PropTypes.any.isRequired, - onRangeClick: PropTypes.func, - - measureColors: PropTypes.any.isRequired, - measureSize: PropTypes.number.isRequired, - measureBorderWidth: PropTypes.number.isRequired, - measureBorderColor: PropTypes.any.isRequired, - onMeasureClick: PropTypes.func, - - markerColors: PropTypes.any.isRequired, - markerSize: PropTypes.number.isRequired, - onMarkerClick: PropTypes.func, - - axisPosition: PropTypes.oneOf(['before', 'after']).isRequired, - - theme: themePropType.isRequired, - - overrides: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string.isRequired, - reverse: PropTypes.bool, - rangeColors: PropTypes.any, - rangeBorderWidth: PropTypes.number, - rangeBorderColor: PropTypes.any, - measureColors: PropTypes.any, - measureBorderWidth: PropTypes.number, - measureBorderColor: PropTypes.any, - axis: PropTypes.shape({ - position: PropTypes.oneOf(['before', 'after']), - min: PropTypes.number, - max: PropTypes.number, - }), - }) - ).isRequired, -} - -export const BulletPropTypes = { - ...commonPropTypes, - role: PropTypes.string.isRequired, -} - -const commonDefaultProps = { - layout: BulletItem.defaultProps.layout, - reverse: BulletItem.defaultProps.reverse, - spacing: 30, - titlePosition: BulletItem.defaultProps.titlePosition, - titleAlign: BulletItem.defaultProps.titleAlign, - titleOffsetX: BulletItem.defaultProps.titleOffsetX, - titleOffsetY: BulletItem.defaultProps.titleOffsetY, - titleRotation: BulletItem.defaultProps.titleRotation, - rangeBorderWidth: 0, - rangeBorderColor: { from: 'color' }, - measureSize: 0.4, - measureBorderWidth: 0, - measureBorderColor: { from: 'color' }, - markerSize: 0.6, - markerColors: BulletItem.defaultProps.markerColors, - axisPosition: BulletItem.defaultProps.axisPosition, - rangeColors: BulletItem.defaultProps.rangeColors, - measureColors: BulletItem.defaultProps.measureColors, - isInteractive: true, - onClick: noop, - overrides: [], -} - -export const BulletDefaultProps = { - ...commonDefaultProps, - role: 'img', -} diff --git a/packages/bullet/src/props.ts b/packages/bullet/src/props.ts new file mode 100644 index 000000000..cde0188c0 --- /dev/null +++ b/packages/bullet/src/props.ts @@ -0,0 +1,29 @@ +import BulletMarkersItem from './BulletMarkersItem' +import BulletRectsItem from './BulletRectsItem' + +export const defaultProps = { + layout: 'horizontal', + reverse: false, + spacing: 30, + axisPosition: 'after', + titlePosition: 'before', + titleAlign: 'middle', + titleRotation: 0, + titleOffsetX: 0, + titleOffsetY: 0, + rangeComponent: BulletRectsItem, + rangeColors: 'seq:cool', + measureComponent: BulletRectsItem, + measureColors: 'seq:red_purple', + markers: [], + markerComponent: BulletMarkersItem, + markerColors: 'seq:red_purple', + rangeBorderWidth: 0, + rangeBorderColor: { from: 'color' }, + measureSize: 0.4, + measureBorderWidth: 0, + measureBorderColor: { from: 'color' }, + markerSize: 0.6, + isInteractive: true, + role: 'img', +} as const diff --git a/packages/bullet/src/types.ts b/packages/bullet/src/types.ts new file mode 100644 index 000000000..8e8fd8848 --- /dev/null +++ b/packages/bullet/src/types.ts @@ -0,0 +1,184 @@ +import * as React from 'react' +import { Box, Dimensions, Theme, Colors, MotionProps } from '@nivo/core' +import { ScaleLinear } from 'd3-scale' + +export type DatumId = string +export type DatumValue = number + +export type WithDatumId = R & { id: DatumId } + +type Point = { + x: number + y: number +} + +export interface DefaultRawDatum { + id: DatumId + title?: React.ReactNode + ranges: number[] + measures: number[] + markers?: number[] +} + +export type EnhancedDatum = DefaultRawDatum & { + scale: ScaleLinear +} + +export interface ComputedRangeDatum { + index: number + v0: number + v1: number + color: string +} + +export interface ComputedMeasuresDatum { + index: number + v0: number + v1: number + color: string +} + +export interface ComputedMarkersDatum { + index: number + value: number + color: string +} + +export type MouseEventHandler = (datum: R, event: React.MouseEvent) => void + +export interface DataProps { + data: T[] +} + +export type WithDimensions = Dimensions & { + outerWidth: number + outerHeight: number +} + +export type CommonBulletProps = WithDimensions & { + margin: Box + + layout: 'horizontal' | 'vertical' + reverse: boolean + spacing: number + + titlePosition: 'before' | 'after' + titleAlign: 'start' | 'middle' | 'end' + titleOffsetX: number + titleOffsetY: number + titleRotation: number + + rangeComponent: React.ComponentType + rangeColors: Colors + // rangeBorderWidth: number + // rangeBorderColor: InheritedColorProp + + measureComponent: React.ComponentType + measureColors: Colors + measureSize: number + // measureBorderWidth: number + // measureBorderColor: InheritedColorProp + + markerComponent: React.ComponentType + markerColors: Colors + markerSize: number + + axisPosition: 'before' | 'after' + + theme: Theme + + role: string +} + +export type BulletHandlers = { + onRangeClick?: MouseEventHandler, SVGRectElement> + onMeasureClick?: MouseEventHandler, SVGRectElement> + onMarkerClick?: MouseEventHandler, SVGLineElement> +} + +export type BulletSvgProps = DataProps & + Partial & + BulletHandlers & + MotionProps + +type BulletMouseEvent = (event: React.MouseEvent) => void + +export type BulletRectsItemProps = Point & + Dimensions & { + index: number + color: string + data: { + v0: number + v1: number + } + onMouseEnter: BulletMouseEvent + onMouseMove: BulletMouseEvent + onMouseLeave: BulletMouseEvent + onClick: BulletMouseEvent + } + +export type BulletMarkersItemProps = Point & { + size: number + rotation: number + color: string + data: { + index: number + value: number + color: string + } + onMouseEnter: BulletMouseEvent + onMouseMove: BulletMouseEvent + onMouseLeave: BulletMouseEvent + onClick: BulletMouseEvent +} + +export type BulletRectsProps = Pick & + Dimensions & + Point & + MotionProps & { + scale: ScaleLinear + data: Array> + rects: Array + component: CommonBulletProps['rangeComponent'] + } + +export type BulletMarkersProps = Pick & + Pick & + MotionProps & { + scale: ScaleLinear + markerSize: number + markers: ComputedMarkersDatum[] + component: CommonBulletProps['markerComponent'] + } + +export type TooltipHandlers = { + hideTooltip: () => void + showTooltip: (content: React.ReactNode, event: React.MouseEvent) => void +} + +export type BulletItemProps = Omit< + CommonBulletProps, + | 'outerWidth' + | 'outerHeight' + | 'margin' + | 'spacing' + | 'role' + | 'measureSize' + | 'markerSize' + | 'theme' +> & + TooltipHandlers & + BulletHandlers & + EnhancedDatum & + MotionProps & + Point & { + rangeColorScale: (datum: DatumValue) => string + computedRanges: ComputedRangeDatum[] + measureColorScale: (datum: DatumValue) => string + computedMeasures: ComputedMeasuresDatum[] + markerColorScale: (datum: DatumValue) => string + computedMarkers: ComputedMarkersDatum[] + measureHeight: number + markerHeight: number + theme?: Theme + } diff --git a/packages/bullet/tsconfig.json b/packages/bullet/tsconfig.json new file mode 100644 index 000000000..855b4b2b7 --- /dev/null +++ b/packages/bullet/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.types.json", + "compilerOptions": { + "outDir": "./dist/types", + "rootDir": "./src" + }, + "include": ["src/**/*"] +} diff --git a/tsconfig.monorepo.json b/tsconfig.monorepo.json index cb88b19cf..8a48710c4 100644 --- a/tsconfig.monorepo.json +++ b/tsconfig.monorepo.json @@ -15,6 +15,7 @@ // { "path": "./packages/generators" }, // Charts now + { "path": "./packages/bullet" }, { "path": "./packages/pie" }, ] -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index ead753b40..35e98af2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5914,6 +5914,18 @@ resolved "https://registry.yarnpkg.com/@types/configstore/-/configstore-2.1.1.tgz#cd1e8553633ad3185c3f2f239ecff5d2643e92b6" integrity sha1-zR6FU2M60xhcPy8jns/10mQ+krY= +"@types/d3-scale@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-3.2.1.tgz#b37578c8c7edb6f040e46e7757783aafd2d50e4e" + integrity sha512-j+FryQSVk3GHLqjOX/RsHwGHg4XByJ0xIO1ASBTgzhE9o1tgeV4kEWLOzMzJRembKalflk5F03lEkM+4V6LDrQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-time@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-2.0.0.tgz#831dd093db91f16b83ba980e194bb8e4bcef44d6" + integrity sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ== + "@types/debug@^0.0.30": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-0.0.30.tgz#dc1e40f7af3b9c815013a7860e6252f6352a84df" @@ -6166,6 +6178,13 @@ dependencies: "@types/react" "*" +"@types/react-motion@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.29.tgz#1a22e11bc1452150e8aa58463f72dd921ff10721" + integrity sha512-MD1DbdcDKruR0zz5Z0XIlrkPdjDMgYx0AHhbaoTBpDirUTt8Bd7x6OFE458nEINZxfPW0EcEOw2O8S/WwGNLsA== + dependencies: + "@types/react" "*" + "@types/react-syntax-highlighter@11.0.4": version "11.0.4" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd" @@ -6196,6 +6215,13 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/recompose@^0.30.7": + version "0.30.7" + resolved "https://registry.yarnpkg.com/@types/recompose/-/recompose-0.30.7.tgz#0d47f3da3bdf889a4f36d4ca7531fac1eee1c6bd" + integrity sha512-kEvD7XMguXgG7jJJS//cE1QTbkFj2qDtIPAg1FvXxE8D6jD1C0WabJjT7cVitC7TK0N5I3yp2955hqNFFZV0wg== + dependencies: + "@types/react" "*" + "@types/resolve@0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" From 1423cec8d4bb4d0a83f0dc7ae7b78d0da29bc47d Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 06:51:55 -0600 Subject: [PATCH 02/18] fix(bullet): fix tslint errors --- packages/bullet/src/Bullet.tsx | 14 ++++++-------- packages/bullet/src/BulletItem.tsx | 7 ++----- packages/bullet/src/BulletMarkers.tsx | 2 +- packages/bullet/src/BulletRects.tsx | 6 ++---- packages/bullet/src/compute.ts | 4 ++-- packages/bullet/src/enhance.ts | 4 +--- packages/bullet/src/types.ts | 8 ++------ 7 files changed, 16 insertions(+), 29 deletions(-) diff --git a/packages/bullet/src/Bullet.tsx b/packages/bullet/src/Bullet.tsx index a8f31ced2..0a4696c4c 100644 --- a/packages/bullet/src/Bullet.tsx +++ b/packages/bullet/src/Bullet.tsx @@ -1,7 +1,6 @@ import React, { Component } from 'react' import { scaleLinear } from 'd3-scale' -// @ts-ignore -import setDisplayName from 'recompose/setDisplayName' +import { setDisplayName } from 'recompose' // @ts-ignore import { Container, SvgWrapper } from '@nivo/core' import { defaultProps } from './props' @@ -58,12 +57,11 @@ export class Bullet extends Component { role, } = { height: 0, width: 0, ...defaultProps, ...this.props } - let itemHeight: number - if (layout === 'horizontal') { - itemHeight = (height - spacing * (data.length - 1)) / data.length - } else { - itemHeight = (width - spacing * (data.length - 1)) / data.length - } + const itemHeight = + layout === 'horizontal' + ? (height - spacing * (data.length - 1)) / data.length + : (width - spacing * (data.length - 1)) / data.length + const measureHeight = itemHeight * measureSize const markerHeight = itemHeight * markerSize diff --git a/packages/bullet/src/BulletItem.tsx b/packages/bullet/src/BulletItem.tsx index 48e78f166..8d2d0b5aa 100644 --- a/packages/bullet/src/BulletItem.tsx +++ b/packages/bullet/src/BulletItem.tsx @@ -1,10 +1,7 @@ import React, { Component } from 'react' -import isString from 'lodash/isString' import { Motion, spring } from 'react-motion' -import partial from 'lodash/partial' -import compose from 'recompose/compose' -import withPropsOnChange from 'recompose/withPropsOnChange' -import pure from 'recompose/pure' +import { partial, isString } from 'lodash' +import { compose, withPropsOnChange, pure } from 'recompose' import { Axis } from '@nivo/axes' // @ts-ignore import { getColorScale, withMotion, themePropType, noop } from '@nivo/core' diff --git a/packages/bullet/src/BulletMarkers.tsx b/packages/bullet/src/BulletMarkers.tsx index bc282252c..c3170409f 100644 --- a/packages/bullet/src/BulletMarkers.tsx +++ b/packages/bullet/src/BulletMarkers.tsx @@ -2,7 +2,7 @@ import React, { Component } from 'react' import { TransitionMotion, spring } from 'react-motion' // @ts-ignore import { interpolateColor, getInterpolatedColor } from '@nivo/colors' -import partial from 'lodash/partial' +import { partial } from 'lodash' import { BulletMarkersProps, ComputedMarkersDatum } from './types' type MouseEventWithDatum = ( diff --git a/packages/bullet/src/BulletRects.tsx b/packages/bullet/src/BulletRects.tsx index 284abcaaa..cb0fb650f 100644 --- a/packages/bullet/src/BulletRects.tsx +++ b/packages/bullet/src/BulletRects.tsx @@ -1,9 +1,7 @@ import React, { Component, Fragment } from 'react' -import partial from 'lodash/partial' +import { partial } from 'lodash' import { TransitionMotion, spring } from 'react-motion' -import compose from 'recompose/compose' -import withPropsOnChange from 'recompose/withPropsOnChange' -import pure from 'recompose/pure' +import { compose, withPropsOnChange, pure } from 'recompose' // @ts-ignore import { interpolateColor, getInterpolatedColor } from '@nivo/colors' import { computeRects } from './compute' diff --git a/packages/bullet/src/compute.ts b/packages/bullet/src/compute.ts index e8179da90..ee4f6e310 100644 --- a/packages/bullet/src/compute.ts +++ b/packages/bullet/src/compute.ts @@ -1,9 +1,9 @@ -import last from 'lodash/last' +import { last } from 'lodash' import { BulletRectsProps, ComputedRangeDatum } from './types' // @ts-ignore import { getColorScale } from '@nivo/core' -type ComputeDatum = BulletRectsProps['data'] extends Array ? U : never +type ComputeDatum = BulletRectsProps['data'] extends (infer U)[] ? U : never type ComputeRect = Pick export const stackValues = ( diff --git a/packages/bullet/src/enhance.ts b/packages/bullet/src/enhance.ts index 6df12f7d3..638c69327 100644 --- a/packages/bullet/src/enhance.ts +++ b/packages/bullet/src/enhance.ts @@ -1,6 +1,4 @@ -import compose from 'recompose/compose' -import defaultProps from 'recompose/defaultProps' -import pure from 'recompose/pure' +import { compose, defaultProps, pure } from 'recompose' // @ts-ignore import { withDimensions, withTheme, withMotion } from '@nivo/core' import { defaultProps as bulletDefaultProps } from './props' diff --git a/packages/bullet/src/types.ts b/packages/bullet/src/types.ts index 8e8fd8848..9bcdc116d 100644 --- a/packages/bullet/src/types.ts +++ b/packages/bullet/src/types.ts @@ -70,14 +70,10 @@ export type CommonBulletProps = WithDimensions & { rangeComponent: React.ComponentType rangeColors: Colors - // rangeBorderWidth: number - // rangeBorderColor: InheritedColorProp measureComponent: React.ComponentType measureColors: Colors measureSize: number - // measureBorderWidth: number - // measureBorderColor: InheritedColorProp markerComponent: React.ComponentType markerColors: Colors @@ -137,8 +133,8 @@ export type BulletRectsProps = Pick & Point & MotionProps & { scale: ScaleLinear - data: Array> - rects: Array + data: Pick[] + rects: (Point & Dimensions & { data: ComputedRangeDatum })[] component: CommonBulletProps['rangeComponent'] } From 7c9b23fc1b327f54bbee727619cbd2b203a5211e Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 11:31:33 -0600 Subject: [PATCH 03/18] feat(bullet): remove recompose dependency --- package.json | 1 + packages/bullet/package.json | 6 +- packages/bullet/src/Bullet.tsx | 23 ++++---- packages/bullet/src/BulletItem.tsx | 85 ++++++++++----------------- packages/bullet/src/BulletRects.tsx | 54 ++++++++--------- packages/bullet/src/enhance.ts | 17 ------ packages/bullet/src/index.ts | 1 + packages/bullet/src/props.ts | 11 ++++ packages/bullet/src/types.ts | 16 +---- packages/bullet/tests/.eslintrc.yml | 2 + packages/bullet/tests/Bullet.test.tsx | 35 +++++++++++ packages/core/index.d.ts | 4 ++ yarn.lock | 68 ++++++++++++++++++--- 13 files changed, 184 insertions(+), 139 deletions(-) delete mode 100644 packages/bullet/src/enhance.ts create mode 100644 packages/bullet/tests/.eslintrc.yml create mode 100644 packages/bullet/tests/Bullet.test.tsx diff --git a/package.json b/package.json index 62db372d6..698f18eba 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@storybook/addons": "^6.0.26", "@storybook/react": "^6.0.26", "@storybook/theming": "^6.0.26", + "@types/jest": "^26.0.15", "@types/lodash": "^4.14.149", "@types/react": "^16.9.16", "@typescript-eslint/eslint-plugin": "^4.6.1", diff --git a/packages/bullet/package.json b/packages/bullet/package.json index 1f8f0dbe0..5515e9a73 100644 --- a/packages/bullet/package.json +++ b/packages/bullet/package.json @@ -29,14 +29,12 @@ "@nivo/tooltip": "0.64.0", "d3-scale": "^3.0.0", "lodash": "^4.17.11", - "react-motion": "^0.5.2", - "recompose": "^0.30.0" + "react-motion": "^0.5.2" }, "devDependencies": { "@nivo/core": "*", "@types/d3-scale": "^3.2.1", - "@types/react-motion": "^0.0.29", - "@types/recompose": "^0.30.7" + "@types/react-motion": "^0.0.29" }, "peerDependencies": { "@nivo/core": "^0.64.0", diff --git a/packages/bullet/src/Bullet.tsx b/packages/bullet/src/Bullet.tsx index 0a4696c4c..a7c15debb 100644 --- a/packages/bullet/src/Bullet.tsx +++ b/packages/bullet/src/Bullet.tsx @@ -1,16 +1,12 @@ import React, { Component } from 'react' import { scaleLinear } from 'd3-scale' -import { setDisplayName } from 'recompose' // @ts-ignore -import { Container, SvgWrapper } from '@nivo/core' +import { Container, SvgWrapper, defaultTheme, extendDefaultTheme, defaultMargin } from '@nivo/core' import { defaultProps } from './props' import { BulletSvgProps, TooltipHandlers } from './types' -import enhance from './enhance' import BulletItem from './BulletItem' export class Bullet extends Component { - static displayName = 'Bullet' - render() { const { data, @@ -22,11 +18,9 @@ export class Bullet extends Component { reverse, axisPosition, - margin, - width, - height, - outerWidth, - outerHeight, + margin: _margin, + width: outerWidth, + height: outerHeight, titlePosition, titleAlign, @@ -43,7 +37,7 @@ export class Bullet extends Component { markerComponent, markerColors, - theme, + theme: _theme, animate, motionStiffness, @@ -57,6 +51,11 @@ export class Bullet extends Component { role, } = { height: 0, width: 0, ...defaultProps, ...this.props } + const theme = extendDefaultTheme(defaultTheme, _theme) + const margin = { ...defaultMargin, ..._margin } + const width = outerWidth - margin.left - margin.right + const height = outerHeight - margin.top - margin.bottom + const itemHeight = layout === 'horizontal' ? (height - spacing * (data.length - 1)) / data.length @@ -144,4 +143,4 @@ export class Bullet extends Component { } } -export default setDisplayName(Bullet.displayName)(enhance(Bullet)) +export default Bullet diff --git a/packages/bullet/src/BulletItem.tsx b/packages/bullet/src/BulletItem.tsx index 8d2d0b5aa..17e777824 100644 --- a/packages/bullet/src/BulletItem.tsx +++ b/packages/bullet/src/BulletItem.tsx @@ -1,12 +1,12 @@ import React, { Component } from 'react' import { Motion, spring } from 'react-motion' import { partial, isString } from 'lodash' -import { compose, withPropsOnChange, pure } from 'recompose' import { Axis } from '@nivo/axes' // @ts-ignore -import { getColorScale, withMotion, themePropType, noop } from '@nivo/core' +import { getColorScale, defaultTheme, extendDefaultTheme } from '@nivo/core' import { BasicTooltip } from '@nivo/tooltip' import { stackValues } from './compute' +import { defaultProps } from './props' import BulletMarkers from './BulletMarkers' import BulletRects from './BulletRects' import { BulletItemProps, ComputedRangeDatum, ComputedMarkersDatum, TooltipHandlers } from './types' @@ -113,27 +113,47 @@ class BulletItem extends Component { titleOffsetY, titleRotation, - computedRanges, rangeComponent, + rangeColors, + ranges, - computedMeasures, measureComponent, measureHeight, + measureColors, + measures, - computedMarkers, markerComponent, + markerColors, markerHeight, + markers = [], - theme, + theme: _theme, showTooltip, hideTooltip, - animate, - motionStiffness, - motionDamping, + animate = defaultProps.animate, + motionStiffness = defaultProps.motionStiffness, + motionDamping = defaultProps.motionDamping, } = this.props + const theme = extendDefaultTheme(defaultTheme, _theme) + + const rangeColorScale = getColorScale(rangeColors, scale, true) + const computedRanges = stackValues(ranges, rangeColorScale) + + const measureColorScale = getColorScale(measureColors, scale) + const computedMeasures = stackValues(measures, measureColorScale) + + const markerColorScale = getColorScale(markerColors, scale) + const computedMarkers = markers.map((marker: number, index: number) => ({ + value: marker, + index, + color: markerColorScale( + markerColorScale.type === 'sequential' ? marker : index + ) as string, + })) + const motionProps = { animate, motionStiffness, @@ -290,49 +310,4 @@ class BulletItem extends Component { } } -const EnhancedBulletItem = compose( - withMotion(), - withPropsOnChange(['rangeColors', 'scale'], ({ rangeColors, scale }) => ({ - rangeColorScale: getColorScale(rangeColors, scale, true), - })), - withPropsOnChange(['ranges', 'rangeColorScale'], ({ ranges, rangeColorScale }) => ({ - computedRanges: stackValues(ranges, rangeColorScale), - })), - withPropsOnChange(['measureColors', 'scale'], ({ measureColors, scale }) => ({ - measureColorScale: getColorScale(measureColors, scale), - })), - withPropsOnChange( - ['measures', 'measureColorScale'], - ({ measures, measureColorScale }) => ({ - computedMeasures: stackValues(measures, measureColorScale), - }) - ), - withPropsOnChange(['markerColors', 'scale'], ({ markerColors, scale }) => ({ - markerColorScale: getColorScale(markerColors, scale), - })), - withPropsOnChange( - ['markers', 'markerColorScale'], - ({ markers, markerColorScale }) => ({ - computedMarkers: markers.map((marker: number, index: number) => ({ - value: marker, - index, - color: markerColorScale(markerColorScale.type === 'sequential' ? marker : index), - })), - }) - ), - pure -)(BulletItem as any) as React.ComponentType< - Omit< - BulletItemProps, - | 'computedRanges' - | 'rangeColorScale' - | 'computedMeasures' - | 'measureColorScale' - | 'computedMarkers' - | 'markerColorScale' - > -> - -EnhancedBulletItem.displayName = 'BulletItem' - -export default EnhancedBulletItem +export default BulletItem diff --git a/packages/bullet/src/BulletRects.tsx b/packages/bullet/src/BulletRects.tsx index cb0fb650f..38b7dae95 100644 --- a/packages/bullet/src/BulletRects.tsx +++ b/packages/bullet/src/BulletRects.tsx @@ -1,7 +1,6 @@ -import React, { Component, Fragment } from 'react' +import React, { Component } from 'react' import { partial } from 'lodash' import { TransitionMotion, spring } from 'react-motion' -import { compose, withPropsOnChange, pure } from 'recompose' // @ts-ignore import { interpolateColor, getInterpolatedColor } from '@nivo/colors' import { computeRects } from './compute' @@ -28,7 +27,26 @@ class BulletRects extends Component { } render() { - const { rects, layout, y, component, animate, motionStiffness, motionDamping } = this.props + const { + data, + layout, + y, + component, + animate, + motionStiffness, + motionDamping, + reverse, + scale, + height, + } = this.props + + const rects = computeRects({ + data, + layout, + reverse, + scale, + height, + }) const transform = `translate(${layout === 'horizontal' ? 0 : y},${ layout === 'horizontal' ? y : 0 @@ -74,7 +92,7 @@ class BulletRects extends Component { }))} > {interpolatedStyles => ( - + <> {interpolatedStyles.map(({ key, style, data }) => { const color = getInterpolatedColor(style) @@ -93,7 +111,7 @@ class BulletRects extends Component { onClick: partial(this.handleClick, data), }) })} - + )} @@ -101,28 +119,4 @@ class BulletRects extends Component { } } -const EnhancedBulletRects = compose( - withPropsOnChange( - ['data', 'layout', 'reverse', 'scale', 'height'], - ({ - data, - layout, - reverse, - scale, - height, - }: Pick) => ({ - rects: computeRects({ - data, - layout, - reverse, - scale, - height, - }), - }) - ), - pure -)(BulletRects as any) as React.ComponentClass & EventHandlers> - -EnhancedBulletRects.displayName = 'BulletRects' - -export default EnhancedBulletRects +export default BulletRects diff --git a/packages/bullet/src/enhance.ts b/packages/bullet/src/enhance.ts deleted file mode 100644 index 638c69327..000000000 --- a/packages/bullet/src/enhance.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { compose, defaultProps, pure } from 'recompose' -// @ts-ignore -import { withDimensions, withTheme, withMotion } from '@nivo/core' -import { defaultProps as bulletDefaultProps } from './props' - -const commonEnhancers = [withDimensions(), withTheme()] - -export default (Component: React.ComponentType) => { - switch (Component.displayName) { - case 'Bullet': - return compose( - ...[defaultProps(bulletDefaultProps), ...commonEnhancers, withMotion(), pure] - )(Component) - } - - return Component -} diff --git a/packages/bullet/src/index.ts b/packages/bullet/src/index.ts index bd646ed9b..a8448cba2 100644 --- a/packages/bullet/src/index.ts +++ b/packages/bullet/src/index.ts @@ -2,3 +2,4 @@ export { default as Bullet } from './Bullet' export { default as BulletItem } from './BulletItem' export { default as ResponsiveBullet } from './ResponsiveBullet' export * from './props' +export * from './types' diff --git a/packages/bullet/src/props.ts b/packages/bullet/src/props.ts index cde0188c0..b19935703 100644 --- a/packages/bullet/src/props.ts +++ b/packages/bullet/src/props.ts @@ -1,5 +1,12 @@ import BulletMarkersItem from './BulletMarkersItem' import BulletRectsItem from './BulletRectsItem' +import { + defaultAnimate, + defaultMotionStiffness, + defaultMotionDamping, + // @ts-ignore + defaultMargin, +} from '@nivo/core' export const defaultProps = { layout: 'horizontal', @@ -25,5 +32,9 @@ export const defaultProps = { measureBorderColor: { from: 'color' }, markerSize: 0.6, isInteractive: true, + animate: defaultAnimate, + motionStiffness: defaultMotionStiffness, + motionDamping: defaultMotionDamping, + margin: defaultMargin, role: 'img', } as const diff --git a/packages/bullet/src/types.ts b/packages/bullet/src/types.ts index 9bcdc116d..0d070d677 100644 --- a/packages/bullet/src/types.ts +++ b/packages/bullet/src/types.ts @@ -50,12 +50,7 @@ export interface DataProps { data: T[] } -export type WithDimensions = Dimensions & { - outerWidth: number - outerHeight: number -} - -export type CommonBulletProps = WithDimensions & { +export type CommonBulletProps = Dimensions & { margin: Box layout: 'horizontal' | 'vertical' @@ -133,8 +128,7 @@ export type BulletRectsProps = Pick & Point & MotionProps & { scale: ScaleLinear - data: Pick[] - rects: (Point & Dimensions & { data: ComputedRangeDatum })[] + data: ComputedRangeDatum[] component: CommonBulletProps['rangeComponent'] } @@ -168,12 +162,6 @@ export type BulletItemProps = Omit< EnhancedDatum & MotionProps & Point & { - rangeColorScale: (datum: DatumValue) => string - computedRanges: ComputedRangeDatum[] - measureColorScale: (datum: DatumValue) => string - computedMeasures: ComputedMeasuresDatum[] - markerColorScale: (datum: DatumValue) => string - computedMarkers: ComputedMarkersDatum[] measureHeight: number markerHeight: number theme?: Theme diff --git a/packages/bullet/tests/.eslintrc.yml b/packages/bullet/tests/.eslintrc.yml new file mode 100644 index 000000000..2f8de9aea --- /dev/null +++ b/packages/bullet/tests/.eslintrc.yml @@ -0,0 +1,2 @@ +env: + jest: true diff --git a/packages/bullet/tests/Bullet.test.tsx b/packages/bullet/tests/Bullet.test.tsx new file mode 100644 index 000000000..c66bacccc --- /dev/null +++ b/packages/bullet/tests/Bullet.test.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { mount } from 'enzyme' +import { radiansToDegrees } from '@nivo/core' +import { Bullet } from '../src' + +const sampleData = [ + { + id: 'A', + ranges: [10, 20, 40], + measures: [30], + markers: [20] + }, + { + id: 'B', + ranges: [100], + measures: [20, 50], + markers: [80] + }, + { + id: 'C', + ranges: [50], + measures: [10], + markers: [40] + }, +] + +describe('Bullet', () => { + describe('data', () => { + it('should render and not crash', () => { + const wrapper = mount() + + expect(wrapper.exists()).toBe(true) + }) + }) +}) diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 9d6f4e2ff..482ed13f6 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -196,6 +196,10 @@ declare module '@nivo/core' { defs: Def[] } + export declare const defaultAnimate = true + export declare const defaultMotionStiffness = 90 + export declare const defaultMotionDamping = 15 + export function PatternLines(props: Omit): JSX.Element export function PatternSquares(props: Omit): JSX.Element export function PatternDots(props: Omit): JSX.Element diff --git a/yarn.lock b/yarn.lock index 35e98af2d..2b00edc95 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3947,6 +3947,17 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + "@jimp/bmp@^0.6.8": version "0.6.8" resolved "https://registry.yarnpkg.com/@jimp/bmp/-/bmp-0.6.8.tgz#8abbfd9e26ba17a47fab311059ea9f7dd82005b6" @@ -6036,6 +6047,21 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" +"@types/istanbul-reports@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" + integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^26.0.15": + version "26.0.15" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.15.tgz#12e02c0372ad0548e07b9f4e19132b834cb1effe" + integrity sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog== + dependencies: + jest-diff "^26.0.0" + pretty-format "^26.0.0" + "@types/json-schema@^7.0.3": version "7.0.3" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" @@ -6215,13 +6241,6 @@ "@types/prop-types" "*" csstype "^2.2.0" -"@types/recompose@^0.30.7": - version "0.30.7" - resolved "https://registry.yarnpkg.com/@types/recompose/-/recompose-0.30.7.tgz#0d47f3da3bdf889a4f36d4ca7531fac1eee1c6bd" - integrity sha512-kEvD7XMguXgG7jJJS//cE1QTbkFj2qDtIPAg1FvXxE8D6jD1C0WabJjT7cVitC7TK0N5I3yp2955hqNFFZV0wg== - dependencies: - "@types/react" "*" - "@types/resolve@0.0.8": version "0.0.8" resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" @@ -10857,6 +10876,11 @@ diff-sequences@^26.0.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.0.0.tgz#0760059a5c287637b842bd7085311db7060e88a6" integrity sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg== +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + diff@4.0.2, diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -16036,6 +16060,16 @@ jest-diff@^25.5.0: jest-get-type "^25.2.6" pretty-format "^25.5.0" +jest-diff@^26.0.0: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + jest-diff@^26.0.1: version "26.0.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.0.1.tgz#c44ab3cdd5977d466de69c46929e0e57f89aa1de" @@ -16097,6 +16131,11 @@ jest-get-type@^26.0.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.0.0.tgz#381e986a718998dbfafcd5ec05934be538db4039" integrity sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg== +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + jest-haste-map@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" @@ -20115,6 +20154,16 @@ pretty-format@^25.5.0: ansi-styles "^4.0.0" react-is "^16.12.0" +pretty-format@^26.0.0, pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + pretty-format@^26.0.1: version "26.0.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.0.1.tgz#a4fe54fe428ad2fd3413ca6bbd1ec8c2e277e197" @@ -20858,6 +20907,11 @@ react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.5: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.5.tgz#c54ac229dd66b5afe0de5acbe47647c3da692ff8" integrity sha512-sudt2uq5P/2TznPV4Wtdi+Lnq3yaYW8LfvPKLM9BKD8jJNBkxMVyB0C9/GmVhLw7Jbdmndk/73n7XQGeN9A3QQ== +react-is@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" + integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA== + react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" From a3be3eaec135f34d4b5cd037329a9bbc5aa8db25 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 12:37:40 -0600 Subject: [PATCH 04/18] feat(bullet): convert to functional components --- packages/bullet/package.json | 1 - packages/bullet/src/Bullet.tsx | 276 ++++++------ packages/bullet/src/BulletItem.tsx | 512 ++++++++++------------ packages/bullet/src/BulletMarkers.tsx | 158 +++---- packages/bullet/src/BulletMarkersItem.tsx | 60 ++- packages/bullet/src/BulletRects.tsx | 186 ++++---- packages/bullet/src/BulletRectsItem.tsx | 54 ++- packages/bullet/src/ResponsiveBullet.tsx | 6 +- packages/bullet/src/index.ts | 6 +- packages/bullet/src/props.ts | 4 +- 10 files changed, 590 insertions(+), 673 deletions(-) diff --git a/packages/bullet/package.json b/packages/bullet/package.json index 5515e9a73..78fd551d4 100644 --- a/packages/bullet/package.json +++ b/packages/bullet/package.json @@ -28,7 +28,6 @@ "@nivo/legends": "0.64.0", "@nivo/tooltip": "0.64.0", "d3-scale": "^3.0.0", - "lodash": "^4.17.11", "react-motion": "^0.5.2" }, "devDependencies": { diff --git a/packages/bullet/src/Bullet.tsx b/packages/bullet/src/Bullet.tsx index a7c15debb..1492f4517 100644 --- a/packages/bullet/src/Bullet.tsx +++ b/packages/bullet/src/Bullet.tsx @@ -1,146 +1,142 @@ -import React, { Component } from 'react' +import React from 'react' import { scaleLinear } from 'd3-scale' // @ts-ignore import { Container, SvgWrapper, defaultTheme, extendDefaultTheme, defaultMargin } from '@nivo/core' import { defaultProps } from './props' import { BulletSvgProps, TooltipHandlers } from './types' -import BulletItem from './BulletItem' - -export class Bullet extends Component { - render() { - const { - data, - - layout, - spacing, - measureSize, - markerSize, - reverse, - axisPosition, - - margin: _margin, - width: outerWidth, - height: outerHeight, - - titlePosition, - titleAlign, - titleOffsetX, - titleOffsetY, - titleRotation, - - rangeComponent, - rangeColors, - - measureComponent, - measureColors, - - markerComponent, - markerColors, - - theme: _theme, - - animate, - motionStiffness, - motionDamping, - - isInteractive, - onRangeClick, - onMeasureClick, - onMarkerClick, - - role, - } = { height: 0, width: 0, ...defaultProps, ...this.props } - - const theme = extendDefaultTheme(defaultTheme, _theme) - const margin = { ...defaultMargin, ..._margin } - const width = outerWidth - margin.left - margin.right - const height = outerHeight - margin.top - margin.bottom - - const itemHeight = - layout === 'horizontal' - ? (height - spacing * (data.length - 1)) / data.length - : (width - spacing * (data.length - 1)) / data.length - - const measureHeight = itemHeight * measureSize - const markerHeight = itemHeight * markerSize - - const enhancedData = data.map(d => { - const all = [...d.ranges, ...d.measures, ...(d.markers ?? [])] - - const max = Math.max(...all) - - const min = Math.min(...all, 0) - - const scale = scaleLinear().domain([min, max]) - - if (layout === 'horizontal') { - scale.range(reverse === true ? [width, 0] : [0, width]) - } else { - scale.range(reverse === true ? [0, height] : [height, 0]) - } - - return { - ...d, - scale, - } - }) - - return ( - - {({ showTooltip, hideTooltip }: TooltipHandlers) => ( - - {enhancedData.map((d, i) => ( - - ))} - - )} - - ) - } +import { BulletItem } from './BulletItem' + +export const Bullet = (props: BulletSvgProps) => { + const { + data, + + layout, + spacing, + measureSize, + markerSize, + reverse, + axisPosition, + + margin: _margin, + width: outerWidth, + height: outerHeight, + + titlePosition, + titleAlign, + titleOffsetX, + titleOffsetY, + titleRotation, + + rangeComponent, + rangeColors, + + measureComponent, + measureColors, + + markerComponent, + markerColors, + + theme: _theme, + + animate, + motionStiffness, + motionDamping, + + isInteractive, + onRangeClick, + onMeasureClick, + onMarkerClick, + + role, + } = { height: 0, width: 0, ...defaultProps, ...props } + + const theme = extendDefaultTheme(defaultTheme, _theme) + const margin = { ...defaultMargin, ..._margin } + const width = outerWidth - margin.left - margin.right + const height = outerHeight - margin.top - margin.bottom + + const itemHeight = + layout === 'horizontal' + ? (height - spacing * (data.length - 1)) / data.length + : (width - spacing * (data.length - 1)) / data.length + + const measureHeight = itemHeight * measureSize + const markerHeight = itemHeight * markerSize + + const enhancedData = data.map(d => { + const all = [...d.ranges, ...d.measures, ...(d.markers ?? [])] + + const max = Math.max(...all) + + const min = Math.min(...all, 0) + + const scale = scaleLinear().domain([min, max]) + + if (layout === 'horizontal') { + scale.range(reverse === true ? [width, 0] : [0, width]) + } else { + scale.range(reverse === true ? [0, height] : [height, 0]) + } + + return { + ...d, + scale, + } + }) + + return ( + + {({ showTooltip, hideTooltip }: TooltipHandlers) => ( + + {enhancedData.map((d, i) => ( + + ))} + + )} + + ) } - -export default Bullet diff --git a/packages/bullet/src/BulletItem.tsx b/packages/bullet/src/BulletItem.tsx index 17e777824..ef7088e6d 100644 --- a/packages/bullet/src/BulletItem.tsx +++ b/packages/bullet/src/BulletItem.tsx @@ -1,251 +1,243 @@ -import React, { Component } from 'react' +import React from 'react' import { Motion, spring } from 'react-motion' -import { partial, isString } from 'lodash' import { Axis } from '@nivo/axes' // @ts-ignore import { getColorScale, defaultTheme, extendDefaultTheme } from '@nivo/core' import { BasicTooltip } from '@nivo/tooltip' import { stackValues } from './compute' import { defaultProps } from './props' -import BulletMarkers from './BulletMarkers' -import BulletRects from './BulletRects' -import { BulletItemProps, ComputedRangeDatum, ComputedMarkersDatum, TooltipHandlers } from './types' - -type TooltipEvent = ( - showTooltip: TooltipHandlers['showTooltip'], - datum: Datum, - event: React.MouseEvent -) => void - -type MouseEventWithDatum = ( - datum: Datum, - event: React.MouseEvent -) => void - -class BulletItem extends Component { - handleRangeTooltip: TooltipEvent = ( - showTooltip, - range, - event - ) => { - showTooltip( - - {range.v0} to {range.v1} - - } - enableChip={true} - color={range.color} - />, - event - ) +import { BulletMarkers } from './BulletMarkers' +import { BulletRects } from './BulletRects' +import { BulletItemProps } from './types' + +export const BulletItem = ({ + id, + + scale, + layout, + reverse, + axisPosition, + x, + y, + width, + height, + + title: _title, + titlePosition, + titleAlign, + titleOffsetX, + titleOffsetY, + titleRotation, + + rangeComponent, + rangeColors, + ranges, + + measureComponent, + measureHeight, + measureColors, + measures, + + markerComponent, + markerColors, + markerHeight, + markers = [], + + onRangeClick, + onMeasureClick, + onMarkerClick, + + theme: _theme, + + showTooltip, + hideTooltip, + + animate = defaultProps.animate, + motionStiffness = defaultProps.motionStiffness, + motionDamping = defaultProps.motionDamping, +}: BulletItemProps) => { + const theme = extendDefaultTheme(defaultTheme, _theme) + + const rangeColorScale = getColorScale(rangeColors, scale, true) + const computedRanges = stackValues(ranges, rangeColorScale) + + const measureColorScale = getColorScale(measureColors, scale) + const computedMeasures = stackValues(measures, measureColorScale) + + const markerColorScale = getColorScale(markerColors, scale) + const computedMarkers = markers.map((marker: number, index: number) => ({ + value: marker, + index, + color: markerColorScale(markerColorScale.type === 'sequential' ? marker : index) as string, + })) + + const motionProps = { + animate, + motionStiffness, + motionDamping, } - handleMeasureTooltip: TooltipEvent = ( - showTooltip, - measure, - event - ) => { - showTooltip( - {measure.v1}} - enableChip={true} - color={measure.color} - />, - event - ) + const rangeNodes = ( + { + showTooltip( + + {range.v0} to {range.v1} + + } + enableChip={true} + color={range.color} + />, + event + ) + }} + onMouseLeave={hideTooltip} + onClick={(range, event) => { + onRangeClick?.({ id, ...range }, event) + }} + {...motionProps} + /> + ) + + const markerNodes = ( + { + showTooltip( + {marker.value}} + enableChip={true} + color={marker.color} + />, + event + ) + }} + onMouseLeave={hideTooltip} + onClick={(marker, event) => { + onMarkerClick?.({ id, ...marker }, event) + }} + {...motionProps} + /> + ) + + let axisX = 0 + let axisY = 0 + if (layout === 'horizontal' && axisPosition === 'after') { + axisY = height + } else if (layout === 'vertical' && axisPosition === 'after') { + axisX = height } - handleMarkerTooltip: TooltipEvent = ( - showTooltip, - marker, - event - ) => { - showTooltip( - {marker.value}} - enableChip={true} - color={marker.color} - />, - event - ) - } - - handleRangeClick: MouseEventWithDatum = (range, event) => { - const { id, onRangeClick } = this.props - onRangeClick?.({ id, ...range }, event) - } - - handleMeasureClick: MouseEventWithDatum = ( - measure, - event - ) => { - const { id, onMeasureClick } = this.props - onMeasureClick?.({ id, ...measure }, event) - } - - handleMarkerClick: MouseEventWithDatum = ( - marker, - event - ) => { - const { id, onMarkerClick } = this.props - onMarkerClick?.({ id, ...marker }, event) - } - - render() { - const { - id, - - scale, - layout, - reverse, - axisPosition, - x, - y, - width, - height, - - title: _title, - titlePosition, - titleAlign, - titleOffsetX, - titleOffsetY, - titleRotation, - - rangeComponent, - rangeColors, - ranges, - - measureComponent, - measureHeight, - measureColors, - measures, - - markerComponent, - markerColors, - markerHeight, - markers = [], - - theme: _theme, - - showTooltip, - hideTooltip, - - animate = defaultProps.animate, - motionStiffness = defaultProps.motionStiffness, - motionDamping = defaultProps.motionDamping, - } = this.props - - const theme = extendDefaultTheme(defaultTheme, _theme) - - const rangeColorScale = getColorScale(rangeColors, scale, true) - const computedRanges = stackValues(ranges, rangeColorScale) - - const measureColorScale = getColorScale(measureColors, scale) - const computedMeasures = stackValues(measures, measureColorScale) - - const markerColorScale = getColorScale(markerColors, scale) - const computedMarkers = markers.map((marker: number, index: number) => ({ - value: marker, - index, - color: markerColorScale( - markerColorScale.type === 'sequential' ? marker : index - ) as string, - })) - - const motionProps = { - animate, - motionStiffness, - motionDamping, - } - - const rangeNodes = ( - - ) - - const markerNodes = ( - + - ) - - let axisX = 0 - let axisY = 0 - if (layout === 'horizontal' && axisPosition === 'after') { - axisY = height - } else if (layout === 'vertical' && axisPosition === 'after') { - axisX = height - } + + ) + + const title = _title || id + let titleX + let titleY + if (layout === 'horizontal') { + titleX = titlePosition === 'before' ? titleOffsetX : width + titleOffsetX + titleY = height / 2 + titleOffsetY + } else { + titleX = height / 2 + titleOffsetX + titleY = titlePosition === 'before' ? titleOffsetY : width + titleOffsetY + } - const axis = ( - - + {typeof title === 'string' ? ( + + {title} + + ) : ( + title + )} + + ) + + if (animate !== true) { + return ( + + {rangeNodes} + { + showTooltip( + {measure.v1}} + enableChip={true} + color={measure.color} + />, + event + ) + }} + onMouseLeave={hideTooltip} + onClick={(measure, event) => { + onMeasureClick?.({ id, ...measure }, event) + }} + {...motionProps} /> + {axis} + {markerNodes} + {titleNode} ) + } - const title = _title || id - let titleX - let titleY - if (layout === 'horizontal') { - titleX = titlePosition === 'before' ? titleOffsetX : width + titleOffsetX - titleY = height / 2 + titleOffsetY - } else { - titleX = height / 2 + titleOffsetX - titleY = titlePosition === 'before' ? titleOffsetY : width + titleOffsetY - } - - const titleNode = ( - - {isString(title) ? ( - - {title} - - ) : ( - title - )} - - ) + const springConfig = { + damping: motionDamping, + stiffness: motionStiffness, + } - if (animate !== true) { - return ( - + return ( + + {values => ( + {rangeNodes} { layout={layout} reverse={reverse} x={0} - y={(height - measureHeight) / 2} + y={values.measuresY} width={width} height={measureHeight} component={measureComponent} - onMouseEnter={partial(this.handleMeasureTooltip, showTooltip)} + onMouseEnter={(measure, event) => { + showTooltip( + {measure.v1}} + enableChip={true} + color={measure.color} + />, + event + ) + }} onMouseLeave={hideTooltip} - onClick={this.handleMeasureClick} + onClick={(measure, event) => { + onMeasureClick?.({ id, ...measure }, event) + }} {...motionProps} /> {axis} {markerNodes} {titleNode} - ) - } - - const springConfig = { - damping: motionDamping, - stiffness: motionStiffness, - } - - return ( - - {values => ( - - {rangeNodes} - - {axis} - {markerNodes} - {titleNode} - - )} - - ) - } + )} + + ) } - -export default BulletItem diff --git a/packages/bullet/src/BulletMarkers.tsx b/packages/bullet/src/BulletMarkers.tsx index c3170409f..7599de99f 100644 --- a/packages/bullet/src/BulletMarkers.tsx +++ b/packages/bullet/src/BulletMarkers.tsx @@ -1,8 +1,7 @@ -import React, { Component } from 'react' +import React from 'react' import { TransitionMotion, spring } from 'react-motion' // @ts-ignore import { interpolateColor, getInterpolatedColor } from '@nivo/colors' -import { partial } from 'lodash' import { BulletMarkersProps, ComputedMarkersDatum } from './types' type MouseEventWithDatum = ( @@ -38,97 +37,84 @@ const getPositionGenerator = ({ } } -export default class BulletMarkers extends Component { - handleMouseEnter: MouseEventWithDatum = (data, event) => { - this.props.onMouseEnter(data, event) - } - - handleMouseLeave: MouseEventWithDatum = (data, event) => { - this.props.onMouseLeave(data, event) +export const BulletMarkers = ({ + scale, + layout, + reverse, + markers, + height, + markerSize, + component, + animate, + motionStiffness, + motionDamping, + onMouseEnter, + onMouseLeave, + onClick, +}: BulletMarkersProps & EventHandlers) => { + const getPosition = getPositionGenerator({ layout, reverse, scale, height, markerSize }) + + if (animate !== true) { + return ( + <> + {markers.map(marker => + React.createElement(component, { + key: marker.index, + ...marker, + ...getPosition(marker), + data: marker, + onMouseEnter: event => onMouseEnter(marker, event), + onMouseMove: event => onMouseEnter(marker, event), + onMouseLeave: event => onMouseLeave(marker, event), + onClick: event => onClick(marker, event), + }) + )} + + ) } - handleClick: MouseEventWithDatum = (data, event) => { - this.props.onClick(data, event) + const springConfig = { + damping: motionDamping, + stiffness: motionStiffness, } - render() { - const { - scale, - layout, - reverse, - markers, - height, - markerSize, - component, - animate, - motionStiffness, - motionDamping, - } = this.props - - const getPosition = getPositionGenerator({ layout, reverse, scale, height, markerSize }) - - if (animate !== true) { - return ( + return ( + { + const position = getPosition(marker) + + return { + key: `${i}`, + data: marker, + style: { + x: spring(position.x, springConfig), + y: spring(position.y, springConfig), + size: spring(position.size, springConfig), + rotation: spring(position.rotation, springConfig), + ...interpolateColor(marker.color, springConfig), + }, + } + })} + > + {interpolatedStyles => ( <> - {markers.map(marker => - React.createElement(component, { - key: marker.index, + {interpolatedStyles.map(({ key, style, data: marker }) => { + const color = getInterpolatedColor(style) + + return React.createElement(component, { + key, ...marker, - ...getPosition(marker), + ...style, + color, data: marker, - onMouseEnter: partial(this.handleMouseEnter, marker), - onMouseMove: partial(this.handleMouseEnter, marker), - onMouseLeave: partial(this.handleMouseLeave, marker), - onClick: partial(this.handleClick, marker), + onMouseEnter: event => onMouseEnter(marker, event), + onMouseMove: event => onMouseEnter(marker, event), + onMouseLeave: event => onMouseLeave(marker, event), + onClick: event => onClick(marker, event), }) - )} + })} - ) - } - - const springConfig = { - damping: motionDamping, - stiffness: motionStiffness, - } - - return ( - { - const position = getPosition(marker) - - return { - key: `${i}`, - data: marker, - style: { - x: spring(position.x, springConfig), - y: spring(position.y, springConfig), - size: spring(position.size, springConfig), - rotation: spring(position.rotation, springConfig), - ...interpolateColor(marker.color, springConfig), - }, - } - })} - > - {interpolatedStyles => ( - <> - {interpolatedStyles.map(({ key, style, data: marker }) => { - const color = getInterpolatedColor(style) - - return React.createElement(component, { - key, - ...marker, - ...style, - color, - data: marker, - onMouseEnter: partial(this.handleMouseEnter, marker), - onMouseMove: partial(this.handleMouseEnter, marker), - onMouseLeave: partial(this.handleMouseLeave, marker), - onClick: partial(this.handleClick, marker), - }) - })} - - )} - - ) - } + )} + + ) } diff --git a/packages/bullet/src/BulletMarkersItem.tsx b/packages/bullet/src/BulletMarkersItem.tsx index 9a31c4222..06cc30376 100644 --- a/packages/bullet/src/BulletMarkersItem.tsx +++ b/packages/bullet/src/BulletMarkersItem.tsx @@ -1,35 +1,31 @@ -import React, { PureComponent } from 'react' +import React from 'react' import { BulletMarkersItemProps } from './types' -export default class BulletMarkersItem extends PureComponent { - render() { - const { - x, - y, - size, - rotation, - color, - onMouseEnter, - onMouseMove, - onMouseLeave, - onClick, - } = this.props - - return ( - - ) - } +export const BulletMarkersItem = ({ + x, + y, + size, + rotation, + color, + onMouseEnter, + onMouseMove, + onMouseLeave, + onClick, +}: BulletMarkersItemProps) => { + return ( + + ) } diff --git a/packages/bullet/src/BulletRects.tsx b/packages/bullet/src/BulletRects.tsx index 38b7dae95..7a27ed441 100644 --- a/packages/bullet/src/BulletRects.tsx +++ b/packages/bullet/src/BulletRects.tsx @@ -1,5 +1,4 @@ -import React, { Component } from 'react' -import { partial } from 'lodash' +import React from 'react' import { TransitionMotion, spring } from 'react-motion' // @ts-ignore import { interpolateColor, getInterpolatedColor } from '@nivo/colors' @@ -13,110 +12,95 @@ type MouseEventWithDatum = ( type EventHandlers = Record<'onMouseEnter' | 'onMouseLeave' | 'onClick', MouseEventWithDatum> -class BulletRects extends Component { - handleMouseEnter: MouseEventWithDatum = (data, event) => { - this.props.onMouseEnter(data, event) - } - - handleMouseLeave: MouseEventWithDatum = (data, event) => { - this.props.onMouseLeave(data, event) - } - - handleClick: MouseEventWithDatum = (data, event) => { - this.props.onClick(data, event) - } - - render() { - const { - data, - layout, - y, - component, - animate, - motionStiffness, - motionDamping, - reverse, - scale, - height, - } = this.props - - const rects = computeRects({ - data, - layout, - reverse, - scale, - height, - }) - - const transform = `translate(${layout === 'horizontal' ? 0 : y},${ - layout === 'horizontal' ? y : 0 - })` +export const BulletRects = ({ + data, + layout, + y, + component, + animate, + motionStiffness, + motionDamping, + reverse, + scale, + height, + onMouseEnter, + onMouseLeave, + onClick, +}: BulletRectsProps & EventHandlers) => { + const rects = computeRects({ + data, + layout, + reverse, + scale, + height, + }) - if (animate !== true) { - return ( - - {rects.map(rect => - React.createElement(component, { - key: rect.data.index, - index: rect.data.index, - color: rect.data.color, - ...rect, - onMouseEnter: partial(this.handleMouseEnter, rect.data), - onMouseMove: partial(this.handleMouseEnter, rect.data), - onMouseLeave: partial(this.handleMouseLeave, rect.data), - onClick: partial(this.handleClick, rect.data), - }) - )} - - ) - } - - const springConfig = { - damping: motionDamping, - stiffness: motionStiffness, - } + const transform = `translate(${layout === 'horizontal' ? 0 : y},${ + layout === 'horizontal' ? y : 0 + })` + if (animate !== true) { return ( - ({ - key: `${rect.data.index}`, - data: rect.data, - style: { - x: spring(rect.x, springConfig), - y: spring(rect.y, springConfig), - width: spring(rect.width, springConfig), - height: spring(rect.height, springConfig), - ...interpolateColor(rect.data.color, springConfig), - }, - }))} - > - {interpolatedStyles => ( - <> - {interpolatedStyles.map(({ key, style, data }) => { - const color = getInterpolatedColor(style) - - return React.createElement(component, { - key, - index: Number(key), - data, - x: style.x, - y: style.y, - width: Math.max(style.width, 0), - height: Math.max(style.height, 0), - color, - onMouseEnter: partial(this.handleMouseEnter, data), - onMouseMove: partial(this.handleMouseEnter, data), - onMouseLeave: partial(this.handleMouseLeave, data), - onClick: partial(this.handleClick, data), - }) - })} - - )} - + {rects.map(rect => + React.createElement(component, { + key: rect.data.index, + index: rect.data.index, + color: rect.data.color, + ...rect, + onMouseEnter: event => onMouseEnter(rect.data, event), + onMouseMove: event => onMouseEnter(rect.data, event), + onMouseLeave: event => onMouseLeave(rect.data, event), + onClick: event => onClick(rect.data, event), + }) + )} ) } -} -export default BulletRects + const springConfig = { + damping: motionDamping, + stiffness: motionStiffness, + } + + return ( + + ({ + key: `${rect.data.index}`, + data: rect.data, + style: { + x: spring(rect.x, springConfig), + y: spring(rect.y, springConfig), + width: spring(rect.width, springConfig), + height: spring(rect.height, springConfig), + ...interpolateColor(rect.data.color, springConfig), + }, + }))} + > + {interpolatedStyles => ( + <> + {interpolatedStyles.map(({ key, style, data }) => { + const color = getInterpolatedColor(style) + + return React.createElement(component, { + key, + index: Number(key), + data, + x: style.x, + y: style.y, + width: Math.max(style.width, 0), + height: Math.max(style.height, 0), + color, + onMouseEnter: event => onMouseEnter(data, event), + onMouseMove: event => onMouseEnter(data, event), + onMouseLeave: event => onMouseLeave(data, event), + onClick: event => onClick(data, event), + }) + })} + + )} + + + ) +} diff --git a/packages/bullet/src/BulletRectsItem.tsx b/packages/bullet/src/BulletRectsItem.tsx index 8a2091296..5ccab0e02 100644 --- a/packages/bullet/src/BulletRectsItem.tsx +++ b/packages/bullet/src/BulletRectsItem.tsx @@ -1,32 +1,28 @@ -import React, { PureComponent } from 'react' +import React from 'react' import { BulletRectsItemProps } from './types' -export default class BulletRectsItem extends PureComponent { - render() { - const { - x, - y, - width, - height, - color, - onMouseEnter, - onMouseMove, - onMouseLeave, - onClick, - } = this.props - - return ( - - ) - } +export const BulletRectsItem = ({ + x, + y, + width, + height, + color, + onMouseEnter, + onMouseMove, + onMouseLeave, + onClick, +}: BulletRectsItemProps) => { + return ( + + ) } diff --git a/packages/bullet/src/ResponsiveBullet.tsx b/packages/bullet/src/ResponsiveBullet.tsx index 7517d67cc..d3905beb4 100644 --- a/packages/bullet/src/ResponsiveBullet.tsx +++ b/packages/bullet/src/ResponsiveBullet.tsx @@ -2,12 +2,10 @@ import React from 'react' // @ts-ignore import { ResponsiveWrapper, Dimensions } from '@nivo/core' import { BulletSvgProps } from './types' -import Bullet from './Bullet' +import { Bullet } from './Bullet' -const ResponsiveBullet = (props: Omit) => ( +export const ResponsiveBullet = (props: Omit) => ( {({ width, height }: Dimensions) => } ) - -export default ResponsiveBullet diff --git a/packages/bullet/src/index.ts b/packages/bullet/src/index.ts index a8448cba2..c4fc5c00d 100644 --- a/packages/bullet/src/index.ts +++ b/packages/bullet/src/index.ts @@ -1,5 +1,5 @@ -export { default as Bullet } from './Bullet' -export { default as BulletItem } from './BulletItem' -export { default as ResponsiveBullet } from './ResponsiveBullet' +export * from './Bullet' +export * from './BulletItem' +export * from './ResponsiveBullet' export * from './props' export * from './types' diff --git a/packages/bullet/src/props.ts b/packages/bullet/src/props.ts index b19935703..17601120b 100644 --- a/packages/bullet/src/props.ts +++ b/packages/bullet/src/props.ts @@ -1,5 +1,5 @@ -import BulletMarkersItem from './BulletMarkersItem' -import BulletRectsItem from './BulletRectsItem' +import { BulletMarkersItem } from './BulletMarkersItem' +import { BulletRectsItem } from './BulletRectsItem' import { defaultAnimate, defaultMotionStiffness, From ff9e8dcaea887e48407f08d2ba5c60a74b3c2859 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 12:37:59 -0600 Subject: [PATCH 05/18] fix(bullet): fix website paths --- website/src/data/components/bullet/props.js | 38 ++++++++++----------- website/src/pages/bullet/index.js | 22 ++++++------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/website/src/data/components/bullet/props.js b/website/src/data/components/bullet/props.js index 63b82c4e2..dadcb4ac7 100644 --- a/website/src/data/components/bullet/props.js +++ b/website/src/data/components/bullet/props.js @@ -6,7 +6,7 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import { BulletDefaultProps as defaults } from '@nivo/bullet' +import { defaultProps } from '@nivo/bullet' import { themeProperty, motionProperties, groupProperties } from '../../../lib/componentProperties' const props = [ @@ -73,7 +73,7 @@ const props = [ help: `How to display items.`, type: 'string', required: false, - defaultValue: defaults.layout, + defaultValue: defaultProps.layout, controlType: 'radio', controlOptions: { choices: [ @@ -93,7 +93,7 @@ const props = [ `, type: 'boolean', required: false, - defaultValue: defaults.reverse, + defaultValue: defaultProps.reverse, controlType: 'switch', }, { @@ -109,7 +109,7 @@ const props = [ help: 'define spacing between items.', type: 'number', required: false, - defaultValue: defaults.spacing, + defaultValue: defaultProps.spacing, controlType: 'range', group: 'Base', controlOptions: { @@ -123,7 +123,7 @@ const props = [ help: 'define size of measure related to item size, expressed as a ratio.', type: 'number', required: false, - defaultValue: defaults.measureSize, + defaultValue: defaultProps.measureSize, controlType: 'range', group: 'Base', controlOptions: { @@ -137,7 +137,7 @@ const props = [ help: 'define size of markers related to item size, expressed as a ratio.', type: 'number', required: false, - defaultValue: defaults.markerSize, + defaultValue: defaultProps.markerSize, controlType: 'range', group: 'Base', controlOptions: { @@ -165,7 +165,7 @@ const props = [ `, type: 'string | Function | string[]', required: false, - defaultValue: defaults.rangeColors, + defaultValue: defaultProps.rangeColors, controlType: 'colors', group: 'Style', controlOptions: { @@ -190,7 +190,7 @@ const props = [ `, type: 'string | Function | string[]', required: false, - defaultValue: defaults.measureColors, + defaultValue: defaultProps.measureColors, controlType: 'colors', group: 'Style', controlOptions: { @@ -215,7 +215,7 @@ const props = [ `, type: 'string | Function| string[]', required: false, - defaultValue: defaults.markerColors, + defaultValue: defaultProps.markerColors, controlType: 'colors', group: 'Style', controlOptions: { @@ -227,7 +227,7 @@ const props = [ help: `Where to put axis.`, type: 'string', required: false, - defaultValue: defaults.axisPosition, + defaultValue: defaultProps.axisPosition, controlType: 'radio', group: 'Axes', controlOptions: { @@ -242,7 +242,7 @@ const props = [ help: `Where to put title.`, type: 'string', required: false, - defaultValue: defaults.titlePosition, + defaultValue: defaultProps.titlePosition, controlType: 'radio', group: 'Title', controlOptions: { @@ -257,7 +257,7 @@ const props = [ help: `title alignment.`, type: 'string', required: false, - defaultValue: defaults.titleAlign, + defaultValue: defaultProps.titleAlign, controlType: 'choices', group: 'Title', controlOptions: { @@ -273,7 +273,7 @@ const props = [ help: 'title x offset from bullet edge.', type: 'number', required: false, - defaultValue: defaults.titleOffset, + defaultValue: defaultProps.titleOffset, controlType: 'range', group: 'Title', controlOptions: { @@ -287,7 +287,7 @@ const props = [ help: 'title y offset from bullet edge.', type: 'number', required: false, - defaultValue: defaults.titleOffset, + defaultValue: defaultProps.titleOffset, controlType: 'range', group: 'Title', controlOptions: { @@ -301,7 +301,7 @@ const props = [ help: 'title rotation.', type: 'number', required: false, - defaultValue: defaults.titleRotation, + defaultValue: defaultProps.titleRotation, controlType: 'angle', group: 'Title', controlOptions: { @@ -321,7 +321,7 @@ const props = [ description: ` onClick handler for ranges, will receive range data as first argument & event as second one. - + The data has the following shape: \`\`\` { @@ -332,7 +332,7 @@ const props = [ color: string, } \`\`\` - + \`v1\` is the value of the range while \`v0\` is the value of previous range. `, @@ -347,7 +347,7 @@ const props = [ description: ` onClick handler for measures, will receive measure data as first argument & event as second one. - + The data has the following shape: \`\`\` { @@ -385,7 +385,7 @@ const props = [ \`\`\` `, }, - ...motionProperties(['svg'], defaults), + ...motionProperties(['svg'], defaultProps), ] export const groups = groupProperties(props) diff --git a/website/src/pages/bullet/index.js b/website/src/pages/bullet/index.js index fbf44df9a..016a47e33 100644 --- a/website/src/pages/bullet/index.js +++ b/website/src/pages/bullet/index.js @@ -8,7 +8,7 @@ */ import React from 'react' import shuffle from 'lodash/shuffle' -import { ResponsiveBullet, BulletDefaultProps } from '@nivo/bullet' +import { ResponsiveBullet, defaultProps } from '@nivo/bullet' import { generateBulletData } from '@nivo/generators' import ComponentTemplate from '../../components/components/ComponentTemplate' import meta from '../../data/components/bullet/meta.yml' @@ -29,20 +29,20 @@ const initialProperties = { bottom: 50, left: 90, }, - layout: BulletDefaultProps.layout, - reverse: BulletDefaultProps.reverse, + layout: defaultProps.layout, + reverse: defaultProps.reverse, spacing: 46, - titlePosition: BulletDefaultProps.titlePosition, + titlePosition: defaultProps.titlePosition, titleAlign: 'start', titleOffsetX: -70, - titleOffsetY: BulletDefaultProps.titleOffsetY, - titleRotation: BulletDefaultProps.titleRotation, + titleOffsetY: defaultProps.titleOffsetY, + titleRotation: defaultProps.titleRotation, measureSize: 0.2, markerSize: 0.6, - axisPosition: BulletDefaultProps.axisPosition, - rangeColors: BulletDefaultProps.rangeColors, - measureColors: BulletDefaultProps.measureColors, - markerColors: BulletDefaultProps.markerColors, + axisPosition: defaultProps.axisPosition, + rangeColors: defaultProps.rangeColors, + measureColors: defaultProps.measureColors, + markerColors: defaultProps.markerColors, animate: true, motionStiffness: 90, motionDamping: 12, @@ -58,7 +58,7 @@ const Bullet = () => { currentFlavor="svg" properties={groups} initialProperties={initialProperties} - defaultProperties={BulletDefaultProps} + defaultProperties={defaultProps} generateData={generateData} > {(properties, data, theme, logAction) => { From 2db58c0821700ca331f461df902e677a70384348 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 15:49:26 -0600 Subject: [PATCH 06/18] feat(bullet): switch from react-motion to react-spring --- packages/bullet/package.json | 5 +- packages/bullet/src/Bullet.tsx | 9 +- packages/bullet/src/BulletItem.tsx | 142 ++++++-------------- packages/bullet/src/BulletMarkers.tsx | 108 +++++++-------- packages/bullet/src/BulletMarkersItem.tsx | 15 +-- packages/bullet/src/BulletRects.tsx | 135 ++++++++++--------- packages/bullet/src/BulletRectsItem.tsx | 13 +- packages/bullet/src/compute.ts | 9 +- packages/bullet/src/props.ts | 13 +- packages/bullet/src/types.ts | 43 +++++- packages/core/index.d.ts | 19 +++ packages/core/src/components/Container.js | 3 + website/src/data/components/bullet/props.js | 2 +- website/src/pages/bullet/index.js | 5 +- yarn.lock | 7 - 15 files changed, 240 insertions(+), 288 deletions(-) diff --git a/packages/bullet/package.json b/packages/bullet/package.json index 78fd551d4..de19d5be5 100644 --- a/packages/bullet/package.json +++ b/packages/bullet/package.json @@ -28,12 +28,11 @@ "@nivo/legends": "0.64.0", "@nivo/tooltip": "0.64.0", "d3-scale": "^3.0.0", - "react-motion": "^0.5.2" + "react-spring": "^8.0.27" }, "devDependencies": { "@nivo/core": "*", - "@types/d3-scale": "^3.2.1", - "@types/react-motion": "^0.0.29" + "@types/d3-scale": "^3.2.1" }, "peerDependencies": { "@nivo/core": "^0.64.0", diff --git a/packages/bullet/src/Bullet.tsx b/packages/bullet/src/Bullet.tsx index 1492f4517..6c751e7f9 100644 --- a/packages/bullet/src/Bullet.tsx +++ b/packages/bullet/src/Bullet.tsx @@ -39,8 +39,7 @@ export const Bullet = (props: BulletSvgProps) => { theme: _theme, animate, - motionStiffness, - motionDamping, + motionConfig, isInteractive, onRangeClick, @@ -89,8 +88,7 @@ export const Bullet = (props: BulletSvgProps) => { isInteractive={isInteractive} theme={theme} animate={animate} - motionStiffness={motionStiffness} - motionDamping={motionDamping} + motionConfig={motionConfig} > {({ showTooltip, hideTooltip }: TooltipHandlers) => ( { markerColors={markerColors} theme={theme} axisPosition={axisPosition} - animate={animate} - motionStiffness={motionStiffness} - motionDamping={motionDamping} showTooltip={showTooltip} hideTooltip={hideTooltip} onRangeClick={onRangeClick} diff --git a/packages/bullet/src/BulletItem.tsx b/packages/bullet/src/BulletItem.tsx index ef7088e6d..e5b1f586e 100644 --- a/packages/bullet/src/BulletItem.tsx +++ b/packages/bullet/src/BulletItem.tsx @@ -1,11 +1,10 @@ import React from 'react' -import { Motion, spring } from 'react-motion' +import { AnimatedValue, useSpring, animated } from 'react-spring' import { Axis } from '@nivo/axes' // @ts-ignore -import { getColorScale, defaultTheme, extendDefaultTheme } from '@nivo/core' +import { getColorScale, defaultTheme, extendDefaultTheme, useMotionConfig } from '@nivo/core' import { BasicTooltip } from '@nivo/tooltip' import { stackValues } from './compute' -import { defaultProps } from './props' import { BulletMarkers } from './BulletMarkers' import { BulletRects } from './BulletRects' import { BulletItemProps } from './types' @@ -51,10 +50,6 @@ export const BulletItem = ({ showTooltip, hideTooltip, - - animate = defaultProps.animate, - motionStiffness = defaultProps.motionStiffness, - motionDamping = defaultProps.motionDamping, }: BulletItemProps) => { const theme = extendDefaultTheme(defaultTheme, _theme) @@ -71,12 +66,6 @@ export const BulletItem = ({ color: markerColorScale(markerColorScale.type === 'sequential' ? marker : index) as string, })) - const motionProps = { - animate, - motionStiffness, - motionDamping, - } - const rangeNodes = ( { onRangeClick?.({ id, ...range }, event) }} - {...motionProps} /> ) @@ -133,7 +121,6 @@ export const BulletItem = ({ onClick={(marker, event) => { onMarkerClick?.({ id, ...marker }, event) }} - {...motionProps} /> ) @@ -186,90 +173,49 @@ export const BulletItem = ({ ) - if (animate !== true) { - return ( - - {rangeNodes} - { - showTooltip( - {measure.v1}} - enableChip={true} - color={measure.color} - />, - event - ) - }} - onMouseLeave={hideTooltip} - onClick={(measure, event) => { - onMeasureClick?.({ id, ...measure }, event) - }} - {...motionProps} - /> - {axis} - {markerNodes} - {titleNode} - - ) - } - - const springConfig = { - damping: motionDamping, - stiffness: motionStiffness, - } + const { animate, config: springConfig } = useMotionConfig() + const animatedProps = useSpring({ + measuresY: (height - measureHeight) / 2, + transform: `translate(${x},${y})`, + config: springConfig, + immediate: !animate, + }) as AnimatedValue<{ + measuresY: number; + transform: string; + }> return ( - - {values => ( - - {rangeNodes} - { - showTooltip( - {measure.v1}} - enableChip={true} - color={measure.color} - />, - event - ) - }} - onMouseLeave={hideTooltip} - onClick={(measure, event) => { - onMeasureClick?.({ id, ...measure }, event) - }} - {...motionProps} - /> - {axis} - {markerNodes} - {titleNode} - - )} - + + {rangeNodes} + { + showTooltip( + {measure.v1}} + enableChip={true} + color={measure.color} + />, + event + ) + }} + onMouseLeave={hideTooltip} + onClick={(measure, event) => { + onMeasureClick?.({ id, ...measure }, event) + }} + /> + {axis} + {markerNodes} + {titleNode} + ) } diff --git a/packages/bullet/src/BulletMarkers.tsx b/packages/bullet/src/BulletMarkers.tsx index 7599de99f..5de1a98e0 100644 --- a/packages/bullet/src/BulletMarkers.tsx +++ b/packages/bullet/src/BulletMarkers.tsx @@ -1,8 +1,15 @@ import React from 'react' -import { TransitionMotion, spring } from 'react-motion' +import { useTransition } from 'react-spring' +// @ts-ignore +import { useMotionConfig } from '@nivo/core' // @ts-ignore import { interpolateColor, getInterpolatedColor } from '@nivo/colors' -import { BulletMarkersProps, ComputedMarkersDatum } from './types' +import { + BulletMarkersProps, + ComputedMarkersDatum, + MarkerWithPosition, + PositionWithColor, +} from './types' type MouseEventWithDatum = ( datum: ComputedMarkersDatum, @@ -45,76 +52,51 @@ export const BulletMarkers = ({ height, markerSize, component, - animate, - motionStiffness, - motionDamping, onMouseEnter, onMouseLeave, onClick, }: BulletMarkersProps & EventHandlers) => { const getPosition = getPositionGenerator({ layout, reverse, scale, height, markerSize }) - if (animate !== true) { - return ( - <> - {markers.map(marker => - React.createElement(component, { - key: marker.index, - ...marker, - ...getPosition(marker), - data: marker, - onMouseEnter: event => onMouseEnter(marker, event), - onMouseMove: event => onMouseEnter(marker, event), - onMouseLeave: event => onMouseLeave(marker, event), - onClick: event => onClick(marker, event), - }) - )} - - ) - } - - const springConfig = { - damping: motionDamping, - stiffness: motionStiffness, - } + const { animate, config: springConfig } = useMotionConfig() + const transitions = useTransition( + markers.map(marker => ({ ...marker, position: getPosition(marker) })), + markers.map(marker => `${marker.index}`), + { + enter: ({ color, position }: MarkerWithPosition) => ({ + color, + transform: `rotate(${position.rotation}, ${position.x}, ${position.y})`, + x: position.x, + y1: position.y - position.size / 2, + y2: position.y + position.size / 2, + }), + update: ({ color, position }: MarkerWithPosition) => ({ + color, + transform: `rotate(${position.rotation}, ${position.x}, ${position.y})`, + x: position.x, + y1: position.y - position.size / 2, + y2: position.y + position.size / 2, + }), + config: springConfig, + immediate: !animate, + } as any + ) return ( - { - const position = getPosition(marker) - - return { - key: `${i}`, + <> + {transitions.map(({ item: { position, ...marker }, props, key }) => + React.createElement(component, { + key, + ...marker, + ...position, + animatedProps: props, data: marker, - style: { - x: spring(position.x, springConfig), - y: spring(position.y, springConfig), - size: spring(position.size, springConfig), - rotation: spring(position.rotation, springConfig), - ...interpolateColor(marker.color, springConfig), - }, - } - })} - > - {interpolatedStyles => ( - <> - {interpolatedStyles.map(({ key, style, data: marker }) => { - const color = getInterpolatedColor(style) - - return React.createElement(component, { - key, - ...marker, - ...style, - color, - data: marker, - onMouseEnter: event => onMouseEnter(marker, event), - onMouseMove: event => onMouseEnter(marker, event), - onMouseLeave: event => onMouseLeave(marker, event), - onClick: event => onClick(marker, event), - }) - })} - + onMouseEnter: event => onMouseEnter(marker, event), + onMouseMove: event => onMouseEnter(marker, event), + onMouseLeave: event => onMouseLeave(marker, event), + onClick: event => onClick(marker, event), + }) )} - + ) } diff --git a/packages/bullet/src/BulletMarkersItem.tsx b/packages/bullet/src/BulletMarkersItem.tsx index 06cc30376..c5ac43ac9 100644 --- a/packages/bullet/src/BulletMarkersItem.tsx +++ b/packages/bullet/src/BulletMarkersItem.tsx @@ -1,24 +1,21 @@ import React from 'react' import { BulletMarkersItemProps } from './types' +import { animated } from 'react-spring' export const BulletMarkersItem = ({ - x, - y, - size, - rotation, - color, + animatedProps: { color, transform, x, y1, y2 }, onMouseEnter, onMouseMove, onMouseLeave, onClick, }: BulletMarkersItemProps) => { return ( - export const BulletRects = ({ + animatedProps, data, layout, y, component, - animate, - motionStiffness, - motionDamping, reverse, scale, height, @@ -35,72 +40,68 @@ export const BulletRects = ({ height, }) - const transform = `translate(${layout === 'horizontal' ? 0 : y},${ - layout === 'horizontal' ? y : 0 - })` + const getTransform = (value: number) => + `translate(${layout === 'horizontal' ? 0 : value},${layout === 'horizontal' ? value : 0})` - if (animate !== true) { - return ( - - {rects.map(rect => - React.createElement(component, { - key: rect.data.index, - index: rect.data.index, - color: rect.data.color, - ...rect, - onMouseEnter: event => onMouseEnter(rect.data, event), - onMouseMove: event => onMouseEnter(rect.data, event), - onMouseLeave: event => onMouseLeave(rect.data, event), - onClick: event => onClick(rect.data, event), - }) - )} - - ) - } + const transform = animatedProps?.measuresY.interpolate(getTransform) ?? getTransform(y) - const springConfig = { - damping: motionDamping, - stiffness: motionStiffness, - } + const { animate, config: springConfig } = useMotionConfig() + const transitions = useTransition( + rects, + rects.map(rect => `${rect.data.index}`), + { + initial: (rect: BulletRectComputedRect) => ({ + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + color: rect.data.color, + }), + from: (rect: BulletRectComputedRect) => ({ + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + color: rect.data.color, + }), + enter: (rect: BulletRectComputedRect) => ({ + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + color: rect.data.color, + }), + update: (rect: BulletRectComputedRect) => ({ + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + color: rect.data.color, + }), + config: springConfig, + immediate: !animate, + } as any + ) return ( - - ({ - key: `${rect.data.index}`, + + {transitions.map(({ item: rect, props, key }) => + React.createElement(component, { + key, + index: Number(key), + animatedProps: props, data: rect.data, - style: { - x: spring(rect.x, springConfig), - y: spring(rect.y, springConfig), - width: spring(rect.width, springConfig), - height: spring(rect.height, springConfig), - ...interpolateColor(rect.data.color, springConfig), - }, - }))} - > - {interpolatedStyles => ( - <> - {interpolatedStyles.map(({ key, style, data }) => { - const color = getInterpolatedColor(style) - - return React.createElement(component, { - key, - index: Number(key), - data, - x: style.x, - y: style.y, - width: Math.max(style.width, 0), - height: Math.max(style.height, 0), - color, - onMouseEnter: event => onMouseEnter(data, event), - onMouseMove: event => onMouseEnter(data, event), - onMouseLeave: event => onMouseLeave(data, event), - onClick: event => onClick(data, event), - }) - })} - - )} - - + x: props.x.getValue(), + y: props.y.getValue(), + width: props.width.interpolate(value => Math.max(value, 0)).getValue(), + height: props.height.interpolate(value => Math.max(value, 0)).getValue(), + color: props.color.getValue(), + onMouseEnter: event => onMouseEnter(rect.data, event), + onMouseMove: event => onMouseEnter(rect.data, event), + onMouseLeave: event => onMouseLeave(rect.data, event), + onClick: event => onClick(rect.data, event), + }) + )} + ) } diff --git a/packages/bullet/src/BulletRectsItem.tsx b/packages/bullet/src/BulletRectsItem.tsx index 5ccab0e02..4cd616bc3 100644 --- a/packages/bullet/src/BulletRectsItem.tsx +++ b/packages/bullet/src/BulletRectsItem.tsx @@ -1,23 +1,20 @@ import React from 'react' import { BulletRectsItemProps } from './types' +import { animated } from 'react-spring' export const BulletRectsItem = ({ - x, - y, - width, - height, - color, + animatedProps: { x, y, width, height, color }, onMouseEnter, onMouseMove, onMouseLeave, onClick, }: BulletRectsItemProps) => { return ( - Math.max(value, 0))} + height={height.interpolate(value => Math.max(value, 0))} fill={color} onMouseMove={onMouseMove} onMouseEnter={onMouseEnter} diff --git a/packages/bullet/src/compute.ts b/packages/bullet/src/compute.ts index ee4f6e310..75afbdd38 100644 --- a/packages/bullet/src/compute.ts +++ b/packages/bullet/src/compute.ts @@ -3,7 +3,6 @@ import { BulletRectsProps, ComputedRangeDatum } from './types' // @ts-ignore import { getColorScale } from '@nivo/core' -type ComputeDatum = BulletRectsProps['data'] extends (infer U)[] ? U : never type ComputeRect = Pick export const stackValues = ( @@ -32,7 +31,7 @@ export const stackValues = ( export const getComputeRect = ({ layout, reverse, scale, height }: ComputeRect) => { if (layout === 'horizontal') { if (reverse === true) { - return (d: ComputeDatum) => { + return (d: ComputedRangeDatum) => { const x = scale(d.v1) const w = scale(d.v0) - x @@ -40,7 +39,7 @@ export const getComputeRect = ({ layout, reverse, scale, height }: ComputeRect) } } - return (d: ComputeDatum) => { + return (d: ComputedRangeDatum) => { const x = scale(d.v0) const w = scale(d.v1) - x @@ -49,7 +48,7 @@ export const getComputeRect = ({ layout, reverse, scale, height }: ComputeRect) } if (reverse === true) { - return (d: ComputeDatum) => { + return (d: ComputedRangeDatum) => { const y = scale(d.v0) const h = scale(d.v1) - y @@ -57,7 +56,7 @@ export const getComputeRect = ({ layout, reverse, scale, height }: ComputeRect) } } - return (d: ComputeDatum) => { + return (d: ComputedRangeDatum) => { const y = scale(d.v1) const h = scale(d.v0) - y diff --git a/packages/bullet/src/props.ts b/packages/bullet/src/props.ts index 17601120b..ef237eef4 100644 --- a/packages/bullet/src/props.ts +++ b/packages/bullet/src/props.ts @@ -1,12 +1,6 @@ import { BulletMarkersItem } from './BulletMarkersItem' import { BulletRectsItem } from './BulletRectsItem' -import { - defaultAnimate, - defaultMotionStiffness, - defaultMotionDamping, - // @ts-ignore - defaultMargin, -} from '@nivo/core' +import { motionDefaultProps, defaultMargin } from '@nivo/core' export const defaultProps = { layout: 'horizontal', @@ -32,9 +26,8 @@ export const defaultProps = { measureBorderColor: { from: 'color' }, markerSize: 0.6, isInteractive: true, - animate: defaultAnimate, - motionStiffness: defaultMotionStiffness, - motionDamping: defaultMotionDamping, + animate: motionDefaultProps.animate, + motionConfig: motionDefaultProps.config, margin: defaultMargin, role: 'img', } as const diff --git a/packages/bullet/src/types.ts b/packages/bullet/src/types.ts index 0d070d677..585f30614 100644 --- a/packages/bullet/src/types.ts +++ b/packages/bullet/src/types.ts @@ -1,6 +1,7 @@ import * as React from 'react' -import { Box, Dimensions, Theme, Colors, MotionProps } from '@nivo/core' +import { Box, Dimensions, Theme, Colors, ModernMotionProps } from '@nivo/core' import { ScaleLinear } from 'd3-scale' +import { AnimatedValue } from 'react-spring' export type DatumId = string export type DatumValue = number @@ -90,12 +91,20 @@ export type BulletHandlers = { export type BulletSvgProps = DataProps & Partial & BulletHandlers & - MotionProps + ModernMotionProps type BulletMouseEvent = (event: React.MouseEvent) => void +export type BulletRectComputedRect = Point & + Dimensions & { + data: ComputedRangeDatum + } + +export type BulletRectAnimatedProps = Point & Dimensions & Pick + export type BulletRectsItemProps = Point & Dimensions & { + animatedProps: AnimatedValue index: number color: string data: { @@ -109,6 +118,7 @@ export type BulletRectsItemProps = Point & } export type BulletMarkersItemProps = Point & { + animatedProps: AnimatedValue size: number rotation: number color: string @@ -125,16 +135,35 @@ export type BulletMarkersItemProps = Point & { export type BulletRectsProps = Pick & Dimensions & - Point & - MotionProps & { + Point & { + animatedProps?: AnimatedValue<{ + measuresY: number + transform: string + }> scale: ScaleLinear data: ComputedRangeDatum[] component: CommonBulletProps['rangeComponent'] } +export type Position = Point & { + size: number + rotation: number +} + +export type MarkerWithPosition = ComputedMarkersDatum & { + position: Position +} + +export type PositionWithColor = { + color: string + transform: string + x: number + y1: number + y2: number +} + export type BulletMarkersProps = Pick & - Pick & - MotionProps & { + Pick & { scale: ScaleLinear markerSize: number markers: ComputedMarkersDatum[] @@ -160,7 +189,7 @@ export type BulletItemProps = Omit< TooltipHandlers & BulletHandlers & EnhancedDatum & - MotionProps & + ModernMotionProps & Point & { measureHeight: number markerHeight: number diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 482ed13f6..74980f2eb 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -100,6 +100,11 @@ declare module '@nivo/core' { motionStiffness: number }> + export type ModernMotionProps = Partial<{ + animate: boolean + motionConfig: string + }> + export type SvgFillMatcher = (datum: T) => boolean export interface SvgDefsAndFill { defs?: { @@ -200,6 +205,20 @@ declare module '@nivo/core' { export declare const defaultMotionStiffness = 90 export declare const defaultMotionDamping = 15 + export declare const motionDefaultProps = { + animate: true, + stiffness: 90, + damping: 15, + config: 'default', + } + + export declare const defaultMargin = { + top: 0, + right: 0, + bottom: 0, + left: 0, + } + export function PatternLines(props: Omit): JSX.Element export function PatternSquares(props: Omit): JSX.Element export function PatternDots(props: Omit): JSX.Element diff --git a/packages/core/src/components/Container.js b/packages/core/src/components/Container.js index c5c438569..970db6c49 100644 --- a/packages/core/src/components/Container.js +++ b/packages/core/src/components/Container.js @@ -30,6 +30,7 @@ const Container = ({ animate, motionStiffness, motionDamping, + motionConfig, }) => { const containerEl = useRef(null) const [state, setState] = useState({ @@ -103,6 +104,7 @@ const Container = ({ animate={animate} stiffness={motionStiffness} damping={motionDamping} + config={motionConfig} > {content} @@ -119,6 +121,7 @@ Container.propTypes = { animate: PropTypes.bool.isRequired, motionStiffness: PropTypes.number, motionDamping: PropTypes.number, + motionConfig: PropTypes.string, } export default Container diff --git a/website/src/data/components/bullet/props.js b/website/src/data/components/bullet/props.js index dadcb4ac7..3f61c3219 100644 --- a/website/src/data/components/bullet/props.js +++ b/website/src/data/components/bullet/props.js @@ -385,7 +385,7 @@ const props = [ \`\`\` `, }, - ...motionProperties(['svg'], defaultProps), + ...motionProperties(['svg'], defaultProps, 'react-spring'), ] export const groups = groupProperties(props) diff --git a/website/src/pages/bullet/index.js b/website/src/pages/bullet/index.js index 016a47e33..88a376226 100644 --- a/website/src/pages/bullet/index.js +++ b/website/src/pages/bullet/index.js @@ -43,9 +43,8 @@ const initialProperties = { rangeColors: defaultProps.rangeColors, measureColors: defaultProps.measureColors, markerColors: defaultProps.markerColors, - animate: true, - motionStiffness: 90, - motionDamping: 12, + animate: defaultProps.animate, + motionConfig: defaultProps.motionConfig, } const Bullet = () => { diff --git a/yarn.lock b/yarn.lock index 2b00edc95..f661c7a67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6204,13 +6204,6 @@ dependencies: "@types/react" "*" -"@types/react-motion@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.29.tgz#1a22e11bc1452150e8aa58463f72dd921ff10721" - integrity sha512-MD1DbdcDKruR0zz5Z0XIlrkPdjDMgYx0AHhbaoTBpDirUTt8Bd7x6OFE458nEINZxfPW0EcEOw2O8S/WwGNLsA== - dependencies: - "@types/react" "*" - "@types/react-syntax-highlighter@11.0.4": version "11.0.4" resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.4.tgz#d86d17697db62f98046874f62fdb3e53a0bbc4cd" From 1c254b4d24e04a0d5a7e5e267e5d4b5e13f8b131 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 15:56:23 -0600 Subject: [PATCH 07/18] fix(bullet): fix linting errors --- packages/bullet/src/BulletItem.tsx | 4 ++-- packages/bullet/src/compute.ts | 4 ++-- packages/bullet/tests/Bullet.test.tsx | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/bullet/src/BulletItem.tsx b/packages/bullet/src/BulletItem.tsx index e5b1f586e..47c9b29f7 100644 --- a/packages/bullet/src/BulletItem.tsx +++ b/packages/bullet/src/BulletItem.tsx @@ -180,8 +180,8 @@ export const BulletItem = ({ config: springConfig, immediate: !animate, }) as AnimatedValue<{ - measuresY: number; - transform: string; + measuresY: number + transform: string }> return ( diff --git a/packages/bullet/src/compute.ts b/packages/bullet/src/compute.ts index 75afbdd38..ae4b984b5 100644 --- a/packages/bullet/src/compute.ts +++ b/packages/bullet/src/compute.ts @@ -1,4 +1,3 @@ -import { last } from 'lodash' import { BulletRectsProps, ComputedRangeDatum } from './types' // @ts-ignore import { getColorScale } from '@nivo/core' @@ -13,7 +12,8 @@ export const stackValues = ( const normalized = [...values].filter(v => v !== 0).sort((a, b) => a - b) return normalized.reduce((acc, v1, index) => { - const v0 = last(acc)?.v1 ?? 0 + const [last] = acc.slice(-1) + const v0 = last?.v1 ?? 0 const sequentialValue = useAverage === true ? v0 + (v1 - v0) / 2 : v1 return [ diff --git a/packages/bullet/tests/Bullet.test.tsx b/packages/bullet/tests/Bullet.test.tsx index c66bacccc..506bdf4ff 100644 --- a/packages/bullet/tests/Bullet.test.tsx +++ b/packages/bullet/tests/Bullet.test.tsx @@ -1,6 +1,6 @@ import React from 'react' +// tslint:disable-next-line no-implicit-dependencies import { mount } from 'enzyme' -import { radiansToDegrees } from '@nivo/core' import { Bullet } from '../src' const sampleData = [ @@ -8,19 +8,19 @@ const sampleData = [ id: 'A', ranges: [10, 20, 40], measures: [30], - markers: [20] + markers: [20], }, { id: 'B', ranges: [100], measures: [20, 50], - markers: [80] + markers: [80], }, { id: 'C', ranges: [50], measures: [10], - markers: [40] + markers: [40], }, ] From 9a8b97ba4f4063e6288b4ed3fec7f05ff33af9f2 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 16:47:02 -0600 Subject: [PATCH 08/18] refactor(bullet): cleanup some code --- packages/bullet/src/Bullet.tsx | 5 ++- packages/bullet/src/BulletItem.tsx | 51 ++++++++++++++++------------- packages/bullet/src/BulletRects.tsx | 14 -------- packages/bullet/src/types.ts | 10 +----- 4 files changed, 31 insertions(+), 49 deletions(-) diff --git a/packages/bullet/src/Bullet.tsx b/packages/bullet/src/Bullet.tsx index 6c751e7f9..7eb99c22f 100644 --- a/packages/bullet/src/Bullet.tsx +++ b/packages/bullet/src/Bullet.tsx @@ -1,7 +1,7 @@ import React from 'react' import { scaleLinear } from 'd3-scale' // @ts-ignore -import { Container, SvgWrapper, defaultTheme, extendDefaultTheme, defaultMargin } from '@nivo/core' +import { Container, SvgWrapper, defaultMargin, usePartialTheme } from '@nivo/core' import { defaultProps } from './props' import { BulletSvgProps, TooltipHandlers } from './types' import { BulletItem } from './BulletItem' @@ -49,7 +49,7 @@ export const Bullet = (props: BulletSvgProps) => { role, } = { height: 0, width: 0, ...defaultProps, ...props } - const theme = extendDefaultTheme(defaultTheme, _theme) + const theme = usePartialTheme(_theme) const margin = { ...defaultMargin, ..._margin } const width = outerWidth - margin.left - margin.right const height = outerHeight - margin.top - margin.bottom @@ -121,7 +121,6 @@ export const Bullet = (props: BulletSvgProps) => { measureColors={measureColors} markerComponent={markerComponent} markerColors={markerColors} - theme={theme} axisPosition={axisPosition} showTooltip={showTooltip} hideTooltip={hideTooltip} diff --git a/packages/bullet/src/BulletItem.tsx b/packages/bullet/src/BulletItem.tsx index 47c9b29f7..19a25e3ee 100644 --- a/packages/bullet/src/BulletItem.tsx +++ b/packages/bullet/src/BulletItem.tsx @@ -1,8 +1,18 @@ import React from 'react' import { AnimatedValue, useSpring, animated } from 'react-spring' import { Axis } from '@nivo/axes' -// @ts-ignore -import { getColorScale, defaultTheme, extendDefaultTheme, useMotionConfig } from '@nivo/core' +import { + // @ts-ignore + getColorScale, + // @ts-ignore + defaultTheme, + // @ts-ignore + extendDefaultTheme, + // @ts-ignore + useMotionConfig, + // @ts-ignore + useTheme, +} from '@nivo/core' import { BasicTooltip } from '@nivo/tooltip' import { stackValues } from './compute' import { BulletMarkers } from './BulletMarkers' @@ -21,7 +31,7 @@ export const BulletItem = ({ width, height, - title: _title, + title = id, titlePosition, titleAlign, titleOffsetX, @@ -46,12 +56,10 @@ export const BulletItem = ({ onMeasureClick, onMarkerClick, - theme: _theme, - showTooltip, hideTooltip, }: BulletItemProps) => { - const theme = extendDefaultTheme(defaultTheme, _theme) + const theme = useTheme() const rangeColorScale = getColorScale(rangeColors, scale, true) const computedRanges = stackValues(ranges, rangeColorScale) @@ -124,13 +132,8 @@ export const BulletItem = ({ /> ) - let axisX = 0 - let axisY = 0 - if (layout === 'horizontal' && axisPosition === 'after') { - axisY = height - } else if (layout === 'vertical' && axisPosition === 'after') { - axisX = height - } + const axisX = layout === 'vertical' && axisPosition === 'after' ? height : 0 + const axisY = layout === 'horizontal' && axisPosition === 'after' ? height : 0 const axis = ( @@ -144,16 +147,18 @@ export const BulletItem = ({ ) - const title = _title || id - let titleX - let titleY - if (layout === 'horizontal') { - titleX = titlePosition === 'before' ? titleOffsetX : width + titleOffsetX - titleY = height / 2 + titleOffsetY - } else { - titleX = height / 2 + titleOffsetX - titleY = titlePosition === 'before' ? titleOffsetY : width + titleOffsetY - } + const titleX = + layout === 'horizontal' + ? titlePosition === 'before' + ? titleOffsetX + : width + titleOffsetX + : height / 2 + titleOffsetX + const titleY = + layout === 'horizontal' + ? height / 2 + titleOffsetY + : titlePosition === 'before' + ? titleOffsetY + : width + titleOffsetY const titleNode = ( diff --git a/packages/bullet/src/BulletRects.tsx b/packages/bullet/src/BulletRects.tsx index 772b654de..2112f822f 100644 --- a/packages/bullet/src/BulletRects.tsx +++ b/packages/bullet/src/BulletRects.tsx @@ -50,20 +50,6 @@ export const BulletRects = ({ rects, rects.map(rect => `${rect.data.index}`), { - initial: (rect: BulletRectComputedRect) => ({ - x: rect.x, - y: rect.y, - width: rect.width, - height: rect.height, - color: rect.data.color, - }), - from: (rect: BulletRectComputedRect) => ({ - x: rect.x, - y: rect.y, - width: rect.width, - height: rect.height, - color: rect.data.color, - }), enter: (rect: BulletRectComputedRect) => ({ x: rect.x, y: rect.y, diff --git a/packages/bullet/src/types.ts b/packages/bullet/src/types.ts index 585f30614..ae2a2d218 100644 --- a/packages/bullet/src/types.ts +++ b/packages/bullet/src/types.ts @@ -32,13 +32,6 @@ export interface ComputedRangeDatum { color: string } -export interface ComputedMeasuresDatum { - index: number - v0: number - v1: number - color: string -} - export interface ComputedMarkersDatum { index: number value: number @@ -84,7 +77,7 @@ export type CommonBulletProps = Dimensions & { export type BulletHandlers = { onRangeClick?: MouseEventHandler, SVGRectElement> - onMeasureClick?: MouseEventHandler, SVGRectElement> + onMeasureClick?: MouseEventHandler, SVGRectElement> onMarkerClick?: MouseEventHandler, SVGLineElement> } @@ -193,5 +186,4 @@ export type BulletItemProps = Omit< Point & { measureHeight: number markerHeight: number - theme?: Theme } From e9cf609fb51818f58a9248c9c0b74d2c75e7b475 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 17:00:42 -0600 Subject: [PATCH 09/18] feat(bullet): convert stories to typescript --- .storybook/main.js | 4 ++-- .../bullet/stories/{bullet.stories.js => bullet.stories.tsx} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/bullet/stories/{bullet.stories.js => bullet.stories.tsx} (100%) diff --git a/.storybook/main.js b/.storybook/main.js index f752ec2d4..b20a57277 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,9 +1,9 @@ module.exports = { - stories: ['../**/*.stories.js'], + stories: ['../**/*.stories.@(js|tsx)'], addons: [ '@storybook/addon-knobs', '@storybook/addon-actions', '@storybook/addon-links', '@storybook/addon-storysource', ], -}; \ No newline at end of file +}; diff --git a/packages/bullet/stories/bullet.stories.js b/packages/bullet/stories/bullet.stories.tsx similarity index 100% rename from packages/bullet/stories/bullet.stories.js rename to packages/bullet/stories/bullet.stories.tsx From 23321e35b1b4eb832dcf91bf952db516f558eb82 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Fri, 6 Nov 2020 17:13:36 -0600 Subject: [PATCH 10/18] fix(bullet): fix linting errors round 2 --- packages/bullet/stories/bullet.stories.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/bullet/stories/bullet.stories.tsx b/packages/bullet/stories/bullet.stories.tsx index 21823073b..f0e3f43cb 100644 --- a/packages/bullet/stories/bullet.stories.tsx +++ b/packages/bullet/stories/bullet.stories.tsx @@ -1,6 +1,8 @@ import React from 'react' +/* tslint:disable no-implicit-dependencies */ import { storiesOf } from '@storybook/react' import { generateBulletData } from '@nivo/generators' +/* tslint:enable no-implicit-dependencies */ import { Bullet } from '../src' const data = [ From 4aed7c3140a981c93ed92d35a108ad53cf310d17 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Sun, 8 Nov 2020 12:37:44 -0600 Subject: [PATCH 11/18] fix(tooltip): export useTooltip hook in types --- packages/pie/src/PieSlice.tsx | 2 +- packages/tooltip/index.d.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/pie/src/PieSlice.tsx b/packages/pie/src/PieSlice.tsx index fe83394ff..97bbffd98 100644 --- a/packages/pie/src/PieSlice.tsx +++ b/packages/pie/src/PieSlice.tsx @@ -55,7 +55,7 @@ export const PieSlice = ({ const handleMouseLeave = useCallback( event => { onMouseLeave?.(datum, event) - hideTooltip(event) + hideTooltip() }, [onMouseLeave, hideTooltip, datum] ) diff --git a/packages/tooltip/index.d.ts b/packages/tooltip/index.d.ts index 574fb3e96..9d60ec085 100644 --- a/packages/tooltip/index.d.ts +++ b/packages/tooltip/index.d.ts @@ -49,4 +49,16 @@ declare module '@nivo/tooltip' { | 'bottom-left' | 'left' | 'cross' + + export type TooltipAnchor = 'top' | 'right' | 'bottom' | 'left' | 'center' + + export function useTooltip(): { + showTooltipAt: ( + content: JSX.Element, + position: [number, number], + anchor?: TooltipAnchor + ) => void + showTooltipFromEvent: (content: JSX.Element, event: Event) => void + hideTooltip: () => void + } } From 7d60e7555eac9c80d39d239ed01b3314196a62ba Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Sun, 8 Nov 2020 12:39:43 -0600 Subject: [PATCH 12/18] fix(core): add useDimensions hook to types --- packages/core/index.d.ts | 18 ++++++++++++++++-- packages/pie/src/Pie.tsx | 4 ++-- packages/pie/src/PieCanvas.tsx | 4 ++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/core/index.d.ts b/packages/core/index.d.ts index 74980f2eb..a63f3bc84 100644 --- a/packages/core/index.d.ts +++ b/packages/core/index.d.ts @@ -9,12 +9,14 @@ declare module '@nivo/core' { width: number } - export type Box = Partial<{ + export type Margin = { bottom: number left: number right: number top: number - }> + } + + export type Box = Partial export type BoxAlign = | 'center' | 'top-left' @@ -227,4 +229,16 @@ declare module '@nivo/core' { export function degreesToRadians(degrees: number): number export function radiansToDegrees(radians: number): number + + export function useDimensions( + width: number, + height: number, + margin?: Box + ): { + margin: Margin + innerWidth: number + innerHeight: number + outerWidth: number + outerHeight: number + } } diff --git a/packages/pie/src/Pie.tsx b/packages/pie/src/Pie.tsx index d20f23417..b87c4cfd6 100644 --- a/packages/pie/src/Pie.tsx +++ b/packages/pie/src/Pie.tsx @@ -31,8 +31,8 @@ const Pie = ({ innerRadius: innerRadiusRatio = defaultProps.innerRadius, cornerRadius = defaultProps.cornerRadius, - width, - height, + width = 0, + height = 0, margin: partialMargin, colors = defaultProps.colors, diff --git a/packages/pie/src/PieCanvas.tsx b/packages/pie/src/PieCanvas.tsx index 3538a87f8..a91e08f3a 100644 --- a/packages/pie/src/PieCanvas.tsx +++ b/packages/pie/src/PieCanvas.tsx @@ -90,8 +90,8 @@ const PieCanvas = ({ innerRadius: innerRadiusRatio = defaultProps.innerRadius, cornerRadius = defaultProps.cornerRadius, - width, - height, + width = 0, + height = 0, margin: partialMargin, pixelRatio = 1, From 4c4a4f06b119053422a1c213a26deade160e5343 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Sun, 8 Nov 2020 12:40:29 -0600 Subject: [PATCH 13/18] refactor(core): update Container implementation to match withContainer --- .../core/src/components/ConditionalWrapper.js | 19 +++ packages/core/src/components/Container.js | 124 +++++++----------- 2 files changed, 68 insertions(+), 75 deletions(-) create mode 100644 packages/core/src/components/ConditionalWrapper.js diff --git a/packages/core/src/components/ConditionalWrapper.js b/packages/core/src/components/ConditionalWrapper.js new file mode 100644 index 000000000..a3d678189 --- /dev/null +++ b/packages/core/src/components/ConditionalWrapper.js @@ -0,0 +1,19 @@ +import { cloneElement } from 'react' +import PropTypes from 'prop-types' + +// type ConditionalWrapperProps = { +// children: JSX.Element +// condition: boolean +// wrapper: (children: JSX.Element) => JSX.Element +// } + +const ConditionalWrapper = ({ children, condition, wrapper }) => + condition ? cloneElement(wrapper(children)) : children + +ConditionalWrapper.propTypes = { + children: PropTypes.node.isRequired, + condition: PropTypes.bool.isRequired, + wrapper: PropTypes.func.isRequired, +} + +export default ConditionalWrapper diff --git a/packages/core/src/components/Container.js b/packages/core/src/components/Container.js index 970db6c49..85e76e6f0 100644 --- a/packages/core/src/components/Container.js +++ b/packages/core/src/components/Container.js @@ -6,117 +6,91 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -import React, { useRef, useState, useCallback } from 'react' +import React, { useRef, useMemo, useCallback } from 'react' import PropTypes from 'prop-types' -import { tooltipContext } from '@nivo/tooltip' +import { tooltipContext, useTooltipHandlers, TooltipWrapper } from '@nivo/tooltip' import noop from '../lib/noop' -import { themeContext } from '../theming' +import { ThemeProvider } from '../theming' import { MotionConfigProvider } from '../motion' +import ConditionalWrapper from './ConditionalWrapper' const containerStyle = { position: 'relative', } -const tooltipStyle = { - pointerEvents: 'none', - position: 'absolute', - zIndex: 10, -} - const Container = ({ children, theme, isInteractive = true, + renderWrapper = true, animate, motionStiffness, motionDamping, motionConfig, }) => { - const containerEl = useRef(null) - const [state, setState] = useState({ - isTooltipVisible: false, - tooltipContent: null, - position: {}, - }) - const showTooltip = useCallback( - (content, event) => { - if (!containerEl) return - - const bounds = containerEl.current.getBoundingClientRect() - - const { clientX, clientY } = event - - const x = clientX - bounds.left - const y = clientY - bounds.top - - const position = {} + const container = useRef(null) + const { + showTooltipAt, + showTooltipFromEvent, + hideTooltip, + isTooltipVisible, + tooltipContent, + tooltipPosition, + tooltipAnchor, + } = useTooltipHandlers(container) - if (x < bounds.width / 2) position.left = x + 20 - else position.right = bounds.width - x + 20 + const showTooltip = useCallback((content, event) => showTooltipFromEvent(content, event), [ + showTooltipFromEvent, + ]) - if (y < bounds.height / 2) position.top = y - 12 - else position.bottom = bounds.height - y - 12 - - setState({ - isTooltipVisible: true, - tooltipContent: content, - position, - }) - }, - [containerEl] - ) - const hideTooltip = useCallback(() => { - setState({ isTooltipVisible: false, tooltipContent: null }) - }) - const { isTooltipVisible, tooltipContent, position } = state - - let content - if (isInteractive === true) { - content = ( -
- {children({ - showTooltip: isInteractive ? showTooltip : noop, - hideTooltip: isInteractive ? hideTooltip : noop, - })} - {isTooltipVisible && ( -
- {tooltipContent} -
- )} -
- ) - } else { - content = children({ + const handlers = useMemo( + () => ({ showTooltip: isInteractive ? showTooltip : noop, hideTooltip: isInteractive ? hideTooltip : noop, - }) - } + }), + [hideTooltip, isInteractive, showTooltip] + ) return ( - + - - {content} + + {/* we should not render the div element if using the HTTP API */} + ( +
+ {children} + {isTooltipVisible && ( + + {tooltipContent} + + )} +
+ )} + > + {typeof children === 'function' ? children(handlers) : children} +
-
+ ) } Container.propTypes = { - children: PropTypes.func.isRequired, + children: PropTypes.oneOf([PropTypes.func, PropTypes.node]).isRequired, isInteractive: PropTypes.bool, + renderWrapper: PropTypes.bool, theme: PropTypes.object.isRequired, animate: PropTypes.bool.isRequired, motionStiffness: PropTypes.number, From d955727b62e5d2a11d8d516e369b4f8ba4a91187 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Sun, 8 Nov 2020 12:42:36 -0600 Subject: [PATCH 14/18] refactor(bullet): respond to PR feedback --- packages/bullet/src/Bullet.tsx | 119 +++++++++------------- packages/bullet/src/BulletItem.tsx | 45 ++++---- packages/bullet/src/BulletMarkers.tsx | 15 +-- packages/bullet/src/BulletMarkersItem.tsx | 9 +- packages/bullet/src/BulletRects.tsx | 42 ++++---- packages/bullet/src/BulletRectsItem.tsx | 9 +- packages/bullet/src/hooks.ts | 38 +++++++ packages/bullet/src/types.ts | 83 +++++++-------- 8 files changed, 195 insertions(+), 165 deletions(-) create mode 100644 packages/bullet/src/hooks.ts diff --git a/packages/bullet/src/Bullet.tsx b/packages/bullet/src/Bullet.tsx index 7eb99c22f..265fb7091 100644 --- a/packages/bullet/src/Bullet.tsx +++ b/packages/bullet/src/Bullet.tsx @@ -1,10 +1,10 @@ import React from 'react' -import { scaleLinear } from 'd3-scale' // @ts-ignore -import { Container, SvgWrapper, defaultMargin, usePartialTheme } from '@nivo/core' +import { Container, SvgWrapper, useDimensions } from '@nivo/core' import { defaultProps } from './props' -import { BulletSvgProps, TooltipHandlers } from './types' +import { BulletSvgProps } from './types' import { BulletItem } from './BulletItem' +import { useEnhancedData } from './hooks' export const Bullet = (props: BulletSvgProps) => { const { @@ -17,9 +17,9 @@ export const Bullet = (props: BulletSvgProps) => { reverse, axisPosition, - margin: _margin, - width: outerWidth, - height: outerHeight, + margin: partialMargin, + width, + height, titlePosition, titleAlign, @@ -36,7 +36,7 @@ export const Bullet = (props: BulletSvgProps) => { markerComponent, markerColors, - theme: _theme, + theme, animate, motionConfig, @@ -49,38 +49,21 @@ export const Bullet = (props: BulletSvgProps) => { role, } = { height: 0, width: 0, ...defaultProps, ...props } - const theme = usePartialTheme(_theme) - const margin = { ...defaultMargin, ..._margin } - const width = outerWidth - margin.left - margin.right - const height = outerHeight - margin.top - margin.bottom + const { margin, innerWidth, innerHeight } = useDimensions(width, height, partialMargin) const itemHeight = layout === 'horizontal' - ? (height - spacing * (data.length - 1)) / data.length - : (width - spacing * (data.length - 1)) / data.length + ? (innerHeight - spacing * (data.length - 1)) / data.length + : (innerWidth - spacing * (data.length - 1)) / data.length const measureHeight = itemHeight * measureSize const markerHeight = itemHeight * markerSize - const enhancedData = data.map(d => { - const all = [...d.ranges, ...d.measures, ...(d.markers ?? [])] - - const max = Math.max(...all) - - const min = Math.min(...all, 0) - - const scale = scaleLinear().domain([min, max]) - - if (layout === 'horizontal') { - scale.range(reverse === true ? [width, 0] : [0, width]) - } else { - scale.range(reverse === true ? [0, height] : [height, 0]) - } - - return { - ...d, - scale, - } + const enhancedData = useEnhancedData(data, { + layout, + reverse, + width: innerWidth, + height: innerHeight, }) return ( @@ -90,47 +73,37 @@ export const Bullet = (props: BulletSvgProps) => { animate={animate} motionConfig={motionConfig} > - {({ showTooltip, hideTooltip }: TooltipHandlers) => ( - - {enhancedData.map((d, i) => ( - - ))} - - )} + + {enhancedData.map((d, i) => ( + + ))} + ) } diff --git a/packages/bullet/src/BulletItem.tsx b/packages/bullet/src/BulletItem.tsx index 19a25e3ee..ef331bf97 100644 --- a/packages/bullet/src/BulletItem.tsx +++ b/packages/bullet/src/BulletItem.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useMemo } from 'react' import { AnimatedValue, useSpring, animated } from 'react-spring' import { Axis } from '@nivo/axes' import { @@ -13,7 +13,7 @@ import { // @ts-ignore useTheme, } from '@nivo/core' -import { BasicTooltip } from '@nivo/tooltip' +import { BasicTooltip, useTooltip } from '@nivo/tooltip' import { stackValues } from './compute' import { BulletMarkers } from './BulletMarkers' import { BulletRects } from './BulletRects' @@ -55,24 +55,33 @@ export const BulletItem = ({ onRangeClick, onMeasureClick, onMarkerClick, - - showTooltip, - hideTooltip, }: BulletItemProps) => { const theme = useTheme() + const { showTooltipFromEvent, hideTooltip } = useTooltip() + + const computedRanges = useMemo(() => { + const rangeColorScale = getColorScale(rangeColors, scale, true) + + return stackValues(ranges, rangeColorScale) + }, [rangeColors, ranges, scale]) + + const computedMeasures = useMemo(() => { + const measureColorScale = getColorScale(measureColors, scale) - const rangeColorScale = getColorScale(rangeColors, scale, true) - const computedRanges = stackValues(ranges, rangeColorScale) + return stackValues(measures, measureColorScale) + }, [measureColors, measures, scale]) - const measureColorScale = getColorScale(measureColors, scale) - const computedMeasures = stackValues(measures, measureColorScale) + const computedMarkers = useMemo(() => { + const markerColorScale = getColorScale(markerColors, scale) - const markerColorScale = getColorScale(markerColors, scale) - const computedMarkers = markers.map((marker: number, index: number) => ({ - value: marker, - index, - color: markerColorScale(markerColorScale.type === 'sequential' ? marker : index) as string, - })) + return markers.map((marker: number, index: number) => ({ + value: marker, + index, + color: markerColorScale( + markerColorScale.type === 'sequential' ? marker : index + ) as string, + })) + }, [markerColors, markers, scale]) const rangeNodes = ( { - showTooltip( + showTooltipFromEvent( @@ -116,7 +125,7 @@ export const BulletItem = ({ markerSize={markerHeight} component={markerComponent} onMouseEnter={(marker, event) => { - showTooltip( + showTooltipFromEvent( {marker.value}} enableChip={true} @@ -204,7 +213,7 @@ export const BulletItem = ({ height={measureHeight} component={measureComponent} onMouseEnter={(measure, event) => { - showTooltip( + showTooltipFromEvent( {measure.v1}} enableChip={true} diff --git a/packages/bullet/src/BulletMarkers.tsx b/packages/bullet/src/BulletMarkers.tsx index 5de1a98e0..d69c91b44 100644 --- a/packages/bullet/src/BulletMarkers.tsx +++ b/packages/bullet/src/BulletMarkers.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useMemo } from 'react' import { useTransition } from 'react-spring' // @ts-ignore import { useMotionConfig } from '@nivo/core' @@ -56,7 +56,10 @@ export const BulletMarkers = ({ onMouseLeave, onClick, }: BulletMarkersProps & EventHandlers) => { - const getPosition = getPositionGenerator({ layout, reverse, scale, height, markerSize }) + const getPosition = useMemo( + () => getPositionGenerator({ layout, reverse, scale, height, markerSize }), + [layout, reverse, scale, height, markerSize] + ) const { animate, config: springConfig } = useMotionConfig() const transitions = useTransition( @@ -91,10 +94,10 @@ export const BulletMarkers = ({ ...position, animatedProps: props, data: marker, - onMouseEnter: event => onMouseEnter(marker, event), - onMouseMove: event => onMouseEnter(marker, event), - onMouseLeave: event => onMouseLeave(marker, event), - onClick: event => onClick(marker, event), + onMouseEnter, + onMouseMove: onMouseEnter, + onMouseLeave, + onClick, }) )} diff --git a/packages/bullet/src/BulletMarkersItem.tsx b/packages/bullet/src/BulletMarkersItem.tsx index c5ac43ac9..0304de897 100644 --- a/packages/bullet/src/BulletMarkersItem.tsx +++ b/packages/bullet/src/BulletMarkersItem.tsx @@ -4,6 +4,7 @@ import { animated } from 'react-spring' export const BulletMarkersItem = ({ animatedProps: { color, transform, x, y1, y2 }, + data, onMouseEnter, onMouseMove, onMouseLeave, @@ -19,10 +20,10 @@ export const BulletMarkersItem = ({ fill="none" stroke={color} strokeWidth="5" - onMouseMove={onMouseMove} - onMouseEnter={onMouseEnter} - onMouseLeave={onMouseLeave} - onClick={onClick} + onMouseMove={event => onMouseMove(data, event)} + onMouseEnter={event => onMouseEnter(data, event)} + onMouseLeave={event => onMouseLeave(data, event)} + onClick={event => onClick(data, event)} /> ) } diff --git a/packages/bullet/src/BulletRects.tsx b/packages/bullet/src/BulletRects.tsx index 2112f822f..41fe34cd7 100644 --- a/packages/bullet/src/BulletRects.tsx +++ b/packages/bullet/src/BulletRects.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useMemo } from 'react' import { useTransition, animated } from 'react-spring' // @ts-ignore import { useMotionConfig } from '@nivo/core' @@ -7,17 +7,17 @@ import { interpolateColor, getInterpolatedColor } from '@nivo/colors' import { computeRects } from './compute' import { BulletRectsProps, - ComputedRangeDatum, + // ComputedRangeDatum, BulletRectComputedRect, BulletRectAnimatedProps, } from './types' -type MouseEventWithDatum = ( - datum: ComputedRangeDatum, - event: React.MouseEvent -) => void +// type MouseEventWithDatum = ( +// datum: ComputedRangeDatum, +// event: React.MouseEvent +// ) => void -type EventHandlers = Record<'onMouseEnter' | 'onMouseLeave' | 'onClick', MouseEventWithDatum> +// type EventHandlers = Record<'onMouseEnter' | 'onMouseLeave' | 'onClick', MouseEventWithDatum> export const BulletRects = ({ animatedProps, @@ -31,14 +31,18 @@ export const BulletRects = ({ onMouseEnter, onMouseLeave, onClick, -}: BulletRectsProps & EventHandlers) => { - const rects = computeRects({ - data, - layout, - reverse, - scale, - height, - }) +}: BulletRectsProps) => { + const rects = useMemo( + () => + computeRects({ + data, + layout, + reverse, + scale, + height, + }), + [data, layout, reverse, scale, height] + ) const getTransform = (value: number) => `translate(${layout === 'horizontal' ? 0 : value},${layout === 'horizontal' ? value : 0})` @@ -82,10 +86,10 @@ export const BulletRects = ({ width: props.width.interpolate(value => Math.max(value, 0)).getValue(), height: props.height.interpolate(value => Math.max(value, 0)).getValue(), color: props.color.getValue(), - onMouseEnter: event => onMouseEnter(rect.data, event), - onMouseMove: event => onMouseEnter(rect.data, event), - onMouseLeave: event => onMouseLeave(rect.data, event), - onClick: event => onClick(rect.data, event), + onMouseEnter, + onMouseMove: onMouseEnter, + onMouseLeave, + onClick, }) )} diff --git a/packages/bullet/src/BulletRectsItem.tsx b/packages/bullet/src/BulletRectsItem.tsx index 4cd616bc3..1f490b8f8 100644 --- a/packages/bullet/src/BulletRectsItem.tsx +++ b/packages/bullet/src/BulletRectsItem.tsx @@ -4,6 +4,7 @@ import { animated } from 'react-spring' export const BulletRectsItem = ({ animatedProps: { x, y, width, height, color }, + data, onMouseEnter, onMouseMove, onMouseLeave, @@ -16,10 +17,10 @@ export const BulletRectsItem = ({ width={width.interpolate(value => Math.max(value, 0))} height={height.interpolate(value => Math.max(value, 0))} fill={color} - onMouseMove={onMouseMove} - onMouseEnter={onMouseEnter} - onMouseLeave={onMouseLeave} - onClick={onClick} + onMouseMove={event => onMouseMove(data, event)} + onMouseEnter={event => onMouseEnter(data, event)} + onMouseLeave={event => onMouseLeave(data, event)} + onClick={event => onClick(data, event)} /> ) } diff --git a/packages/bullet/src/hooks.ts b/packages/bullet/src/hooks.ts new file mode 100644 index 000000000..77b4dd2ae --- /dev/null +++ b/packages/bullet/src/hooks.ts @@ -0,0 +1,38 @@ +import { scaleLinear } from 'd3-scale' +import { useMemo } from 'react' +import { Datum, CommonBulletProps } from './types' + +export const useEnhancedData = ( + data: Datum[], + { + layout, + reverse, + height, + width, + }: Pick +) => { + return useMemo( + () => + data.map(d => { + const all = [...d.ranges, ...d.measures, ...(d.markers ?? [])] + + const max = Math.max(...all) + + const min = Math.min(...all, 0) + + const scale = scaleLinear().domain([min, max]) + + if (layout === 'horizontal') { + scale.range(reverse === true ? [width, 0] : [0, width]) + } else { + scale.range(reverse === true ? [0, height] : [height, 0]) + } + + return { + ...d, + scale, + } + }), + [data, height, layout, reverse, width] + ) +} diff --git a/packages/bullet/src/types.ts b/packages/bullet/src/types.ts index ae2a2d218..43c72a24a 100644 --- a/packages/bullet/src/types.ts +++ b/packages/bullet/src/types.ts @@ -3,7 +3,7 @@ import { Box, Dimensions, Theme, Colors, ModernMotionProps } from '@nivo/core' import { ScaleLinear } from 'd3-scale' import { AnimatedValue } from 'react-spring' -export type DatumId = string +export type DatumId = string | number export type DatumValue = number export type WithDatumId = R & { id: DatumId } @@ -13,7 +13,7 @@ type Point = { y: number } -export interface DefaultRawDatum { +export interface Datum { id: DatumId title?: React.ReactNode ranges: number[] @@ -21,7 +21,7 @@ export interface DefaultRawDatum { markers?: number[] } -export type EnhancedDatum = DefaultRawDatum & { +export type EnhancedDatum = Datum & { scale: ScaleLinear } @@ -38,11 +38,7 @@ export interface ComputedMarkersDatum { color: string } -export type MouseEventHandler = (datum: R, event: React.MouseEvent) => void - -export interface DataProps { - data: T[] -} +export type MouseEventHandler = (datum: D, event: React.MouseEvent) => void export type CommonBulletProps = Dimensions & { margin: Box @@ -81,50 +77,55 @@ export type BulletHandlers = { onMarkerClick?: MouseEventHandler, SVGLineElement> } -export type BulletSvgProps = DataProps & - Partial & +export type BulletSvgProps = Partial & BulletHandlers & - ModernMotionProps + ModernMotionProps & { + data: Datum[] + } -type BulletMouseEvent = (event: React.MouseEvent) => void +type MouseEventWithDatum = ( + datum: D, + event: React.MouseEvent +) => void export type BulletRectComputedRect = Point & Dimensions & { data: ComputedRangeDatum } -export type BulletRectAnimatedProps = Point & Dimensions & Pick +export type BulletRectAnimatedProps = Point & + Dimensions & + Pick -export type BulletRectsItemProps = Point & +export type BulletRectsItemProps = Pick< + BulletRectsProps, + 'onMouseEnter' | 'onMouseLeave' | 'onClick' +> & + Point & Dimensions & { animatedProps: AnimatedValue index: number color: string - data: { - v0: number - v1: number - } - onMouseEnter: BulletMouseEvent - onMouseMove: BulletMouseEvent - onMouseLeave: BulletMouseEvent - onClick: BulletMouseEvent + data: ComputedRangeDatum + onMouseMove: BulletRectsProps['onMouseEnter'] } -export type BulletMarkersItemProps = Point & { - animatedProps: AnimatedValue - size: number - rotation: number - color: string - data: { - index: number - value: number +export type BulletMarkersItemProps = Pick< + BulletMarkersProps, + 'onMouseEnter' | 'onMouseLeave' | 'onClick' +> & + Point & { + animatedProps: AnimatedValue + size: number + rotation: number color: string + data: { + index: number + value: number + color: string + } + onMouseMove: BulletMarkersProps['onMouseEnter'] } - onMouseEnter: BulletMouseEvent - onMouseMove: BulletMouseEvent - onMouseLeave: BulletMouseEvent - onClick: BulletMouseEvent -} export type BulletRectsProps = Pick & Dimensions & @@ -136,6 +137,9 @@ export type BulletRectsProps = Pick & scale: ScaleLinear data: ComputedRangeDatum[] component: CommonBulletProps['rangeComponent'] + onMouseEnter: MouseEventWithDatum + onMouseLeave: MouseEventWithDatum + onClick: MouseEventWithDatum } export type Position = Point & { @@ -161,13 +165,11 @@ export type BulletMarkersProps = Pick & markerSize: number markers: ComputedMarkersDatum[] component: CommonBulletProps['markerComponent'] + onMouseEnter: MouseEventWithDatum + onMouseLeave: MouseEventWithDatum + onClick: MouseEventWithDatum } -export type TooltipHandlers = { - hideTooltip: () => void - showTooltip: (content: React.ReactNode, event: React.MouseEvent) => void -} - export type BulletItemProps = Omit< CommonBulletProps, | 'outerWidth' @@ -179,7 +181,6 @@ export type BulletItemProps = Omit< | 'markerSize' | 'theme' > & - TooltipHandlers & BulletHandlers & EnhancedDatum & ModernMotionProps & From 2aff51d38957904d48982effb913fcfc515ada5b Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Sun, 8 Nov 2020 12:57:29 -0600 Subject: [PATCH 15/18] fix(bullet): fix linting errors round 3 --- packages/bullet/src/types.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/bullet/src/types.ts b/packages/bullet/src/types.ts index 43c72a24a..3cab41746 100644 --- a/packages/bullet/src/types.ts +++ b/packages/bullet/src/types.ts @@ -93,9 +93,7 @@ export type BulletRectComputedRect = Point & data: ComputedRangeDatum } -export type BulletRectAnimatedProps = Point & - Dimensions & - Pick +export type BulletRectAnimatedProps = Point & Dimensions & Pick export type BulletRectsItemProps = Pick< BulletRectsProps, From d63ad7462878ae41d662f102276be03084cdf414 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Sun, 8 Nov 2020 21:39:44 -0600 Subject: [PATCH 16/18] chore(bullet): add proper tests --- packages/bullet/src/BulletItem.tsx | 14 +- packages/bullet/src/BulletMarkers.tsx | 2 - packages/bullet/src/BulletRects.tsx | 16 +- packages/bullet/tests/Bullet.test.tsx | 316 +++++++++++++++++++++++++- 4 files changed, 314 insertions(+), 34 deletions(-) diff --git a/packages/bullet/src/BulletItem.tsx b/packages/bullet/src/BulletItem.tsx index ef331bf97..1cd41e8a0 100644 --- a/packages/bullet/src/BulletItem.tsx +++ b/packages/bullet/src/BulletItem.tsx @@ -1,18 +1,8 @@ import React, { useMemo } from 'react' import { AnimatedValue, useSpring, animated } from 'react-spring' import { Axis } from '@nivo/axes' -import { - // @ts-ignore - getColorScale, - // @ts-ignore - defaultTheme, - // @ts-ignore - extendDefaultTheme, - // @ts-ignore - useMotionConfig, - // @ts-ignore - useTheme, -} from '@nivo/core' +// @ts-ignore +import { getColorScale, useMotionConfig, useTheme } from '@nivo/core' import { BasicTooltip, useTooltip } from '@nivo/tooltip' import { stackValues } from './compute' import { BulletMarkers } from './BulletMarkers' diff --git a/packages/bullet/src/BulletMarkers.tsx b/packages/bullet/src/BulletMarkers.tsx index d69c91b44..3b359a603 100644 --- a/packages/bullet/src/BulletMarkers.tsx +++ b/packages/bullet/src/BulletMarkers.tsx @@ -2,8 +2,6 @@ import React, { useMemo } from 'react' import { useTransition } from 'react-spring' // @ts-ignore import { useMotionConfig } from '@nivo/core' -// @ts-ignore -import { interpolateColor, getInterpolatedColor } from '@nivo/colors' import { BulletMarkersProps, ComputedMarkersDatum, diff --git a/packages/bullet/src/BulletRects.tsx b/packages/bullet/src/BulletRects.tsx index 41fe34cd7..2044c92ea 100644 --- a/packages/bullet/src/BulletRects.tsx +++ b/packages/bullet/src/BulletRects.tsx @@ -2,22 +2,8 @@ import React, { useMemo } from 'react' import { useTransition, animated } from 'react-spring' // @ts-ignore import { useMotionConfig } from '@nivo/core' -// @ts-ignore -import { interpolateColor, getInterpolatedColor } from '@nivo/colors' import { computeRects } from './compute' -import { - BulletRectsProps, - // ComputedRangeDatum, - BulletRectComputedRect, - BulletRectAnimatedProps, -} from './types' - -// type MouseEventWithDatum = ( -// datum: ComputedRangeDatum, -// event: React.MouseEvent -// ) => void - -// type EventHandlers = Record<'onMouseEnter' | 'onMouseLeave' | 'onClick', MouseEventWithDatum> +import { BulletRectsProps, BulletRectComputedRect, BulletRectAnimatedProps } from './types' export const BulletRects = ({ animatedProps, diff --git a/packages/bullet/tests/Bullet.test.tsx b/packages/bullet/tests/Bullet.test.tsx index 506bdf4ff..8fa354f70 100644 --- a/packages/bullet/tests/Bullet.test.tsx +++ b/packages/bullet/tests/Bullet.test.tsx @@ -1,5 +1,4 @@ import React from 'react' -// tslint:disable-next-line no-implicit-dependencies import { mount } from 'enzyme' import { Bullet } from '../src' @@ -20,16 +19,323 @@ const sampleData = [ id: 'C', ranges: [50], measures: [10], - markers: [40], }, ] +const CustomTitle = () => a custom title for A + +const sampleDataWithTitle = [{ ...sampleData[0], title: }, ...sampleData.slice(1)] + describe('Bullet', () => { describe('data', () => { - it('should render and not crash', () => { - const wrapper = mount() + it('should pass data appropriately', () => { + const wrapper = mount() + + const items = wrapper.find('BulletItem') + expect(items).toHaveLength(sampleData.length) + + expect(items.at(0).prop('id')).toEqual('A') + expect(items.at(0).prop('ranges')).toEqual([10, 20, 40]) + expect(items.at(0).prop('measures')).toEqual([30]) + expect(items.at(0).prop('markers')).toEqual([20]) + expect(items.at(0).prop('scale')).toEqual(expect.any(Function)) + + expect(items.at(1).prop('id')).toEqual('B') + expect(items.at(1).prop('ranges')).toEqual([100]) + expect(items.at(1).prop('measures')).toEqual([20, 50]) + expect(items.at(1).prop('markers')).toEqual([80]) + expect(items.at(1).prop('scale')).toEqual(expect.any(Function)) + + expect(items.at(2).prop('id')).toEqual('C') + expect(items.at(2).prop('ranges')).toEqual([50]) + expect(items.at(2).prop('measures')).toEqual([10]) + expect(items.at(2).prop('markers')).toBeUndefined() + expect(items.at(2).prop('scale')).toEqual(expect.any(Function)) + }) + }) + + describe('layout', () => { + it('should use horizontal layout by default', () => { + const wrapper = mount() + const items = wrapper.find('BulletItem') + const ticks = wrapper.find('Axis').first().find('AxisTick') + + expect( + ticks.map(el => el.prop('animatedProps').transform.getValue()).join('; ') + ).toMatchInlineSnapshot( + `"translate(0,0); translate(37.5,0); translate(75,0); translate(112.5,0); translate(150,0); translate(187.5,0); translate(225,0); translate(262.5,0); translate(300,0)"` + ) + expect(items.at(1).prop('x')).toEqual(0) + expect(items.at(1).prop('y')).toEqual(110) + }) + + it('should support vertical layout', () => { + const wrapper = mount( + + ) + const items = wrapper.find('BulletItem') + + expect(items.at(1).prop('x')).toEqual(110) + expect(items.at(1).prop('y')).toEqual(0) + expect(wrapper.find('BulletRects').at(0).prop('layout')).toEqual('vertical') + }) + + it('should support reverse layout', () => { + const wrapper = mount() + const items = wrapper.find('BulletItem') + const ticks = wrapper.find('Axis').first().find('AxisTick') + + expect( + ticks.map(el => el.prop('animatedProps').transform.getValue()).join('; ') + ).toMatchInlineSnapshot( + `"translate(300,0); translate(262.5,0); translate(225,0); translate(187.5,0); translate(150,0); translate(112.5,0); translate(75,0); translate(37.5,0); translate(0,0)"` + ) + expect(items.at(1).prop('x')).toEqual(0) + expect(items.at(1).prop('y')).toEqual(110) + }) + }) + + describe('colors', () => { + it('should support custom colors', () => { + const wrapper = mount( + + ) + const rects = wrapper.find('BulletRectsItem') + + expect(rects.at(0).prop('data').color).toEqual('#aaa') + expect(rects.at(1).prop('data').color).toEqual('#bbb') + expect(rects.at(2).prop('data').color).toEqual('#ccc') + expect(rects.at(3).prop('data').color).toEqual('#ddd') + + const markers = wrapper.find('BulletMarkersItem') + + expect(markers.at(0).prop('data').color).toEqual('#eee') + }) + }) + + describe('interactivity', () => { + it('should support onRangeClick handler', () => { + const onRangeClick = jest.fn() + const wrapper = mount( + + ) + + wrapper.find('BulletRectsItem').at(0).simulate('click') + + expect(onRangeClick).toHaveBeenCalledTimes(1) + expect(onRangeClick).toHaveBeenCalledWith( + { + color: 'rgb(65, 125, 224)', + id: 'A', + index: 0, + v0: 0, + v1: 10, + }, + expect.any(Object) + ) + }) + + it('should support onMeasureClick handler', () => { + const onMeasureClick = jest.fn() + const wrapper = mount( + + ) + + wrapper.find('BulletRectsItem').at(3).simulate('click') + + expect(onMeasureClick).toHaveBeenCalledTimes(1) + expect(onMeasureClick).toHaveBeenCalledWith( + { + color: 'rgb(173, 10, 129)', + id: 'A', + index: 0, + v0: 0, + v1: 30, + }, + expect.any(Object) + ) + }) + + it('should support onMarkerClick handler', () => { + const onMarkerClick = jest.fn() + const wrapper = mount( + + ) + + wrapper.find('BulletMarkersItem').at(0).simulate('click') + + expect(onMarkerClick).toHaveBeenCalledTimes(1) + expect(onMarkerClick).toHaveBeenCalledWith( + { + color: 'rgb(243, 105, 163)', + id: 'A', + index: 0, + value: 20, + }, + expect.any(Object) + ) + }) + }) + + describe('tooltip', () => { + it('should render a tooltip when hovering a range', () => { + const wrapper = mount() + + expect(wrapper.find('TooltipWrapper').exists()).toBeFalsy() + + wrapper.find('BulletRectsItem').at(0).simulate('mouseenter') + + const tooltip = wrapper.find('TooltipWrapper') + + expect(tooltip.exists()).toBeTruthy() + expect(tooltip.text()).toEqual('0 to 10') + }) + + it('should render a tooltip when hovering a measure', () => { + const wrapper = mount() + + expect(wrapper.find('TooltipWrapper').exists()).toBeFalsy() + + wrapper.find('BulletRectsItem').at(3).simulate('mouseenter') + + const tooltip = wrapper.find('TooltipWrapper') + + expect(tooltip.exists()).toBeTruthy() + expect(tooltip.text()).toEqual('30') + }) + + it('should render a tooltip when hovering a marker', () => { + const wrapper = mount() + + expect(wrapper.find('TooltipWrapper').exists()).toBeFalsy() + + wrapper.find('BulletMarkersItem').at(0).simulate('mouseenter') + + const tooltip = wrapper.find('TooltipWrapper') + + expect(tooltip.exists()).toBeTruthy() + expect(tooltip.text()).toEqual('20') + }) + }) + + describe('custom components', () => { + it('should support a custom title component', () => { + const wrapper = mount() + + expect(wrapper.find(CustomTitle).exists()).toBeTruthy() + }) + + it('should support a custom range component', () => { + const CustomRange = () => null + + const wrapper = mount( + + ) + + const customRange = wrapper.find(CustomRange) + const { animatedProps: _animatedProps, ...props } = customRange.at(0).props() + + expect(props).toMatchInlineSnapshot(` + Object { + "color": "rgba(65, 125, 224, 1)", + "data": Object { + "color": "rgb(65, 125, 224)", + "index": 0, + "v0": 0, + "v1": 10, + }, + "height": 80, + "index": 504, + "onClick": [Function], + "onMouseEnter": [Function], + "onMouseLeave": [Function], + "onMouseMove": [Function], + "width": 75, + "x": 0, + "y": 0, + } + `) + }) + + it('should support a custom measure component', () => { + const CustomMeasure = () => null + + const wrapper = mount( + + ) + + const customMeasure = wrapper.find(CustomMeasure) + const { animatedProps: _animatedProps, ...props } = customMeasure.at(0).props() + + expect(props).toMatchInlineSnapshot(` + Object { + "color": "rgba(173, 10, 129, 1)", + "data": Object { + "color": "rgb(173, 10, 129)", + "index": 0, + "v0": 0, + "v1": 30, + }, + "height": 32, + "index": 549, + "onClick": [Function], + "onMouseEnter": [Function], + "onMouseLeave": [Function], + "onMouseMove": [Function], + "width": 225, + "x": 0, + "y": 0, + } + `) + }) + + it('should support a custom marker component', () => { + const CustomMarker = () => null + + const wrapper = mount( + + ) + + const customMarker = wrapper.find(CustomMarker) + const { animatedProps: _animatedProps, ...props } = customMarker.at(0).props() - expect(wrapper.exists()).toBe(true) + expect(props).toMatchInlineSnapshot(` + Object { + "color": "rgb(243, 105, 163)", + "data": Object { + "color": "rgb(243, 105, 163)", + "index": 0, + "value": 20, + }, + "index": 0, + "onClick": [Function], + "onMouseEnter": [Function], + "onMouseLeave": [Function], + "onMouseMove": [Function], + "rotation": 0, + "size": 48, + "value": 20, + "x": 150, + "y": 40, + } + `) }) }) }) From fe0e43cdca08322e7458608229ef6d4178b54198 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Mon, 9 Nov 2020 16:54:55 -0600 Subject: [PATCH 17/18] fix(pie): make dimensions required props --- packages/pie/src/Pie.tsx | 4 ++-- packages/pie/src/PieCanvas.tsx | 4 ++-- packages/pie/src/types.ts | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/pie/src/Pie.tsx b/packages/pie/src/Pie.tsx index b87c4cfd6..d20f23417 100644 --- a/packages/pie/src/Pie.tsx +++ b/packages/pie/src/Pie.tsx @@ -31,8 +31,8 @@ const Pie = ({ innerRadius: innerRadiusRatio = defaultProps.innerRadius, cornerRadius = defaultProps.cornerRadius, - width = 0, - height = 0, + width, + height, margin: partialMargin, colors = defaultProps.colors, diff --git a/packages/pie/src/PieCanvas.tsx b/packages/pie/src/PieCanvas.tsx index a91e08f3a..3538a87f8 100644 --- a/packages/pie/src/PieCanvas.tsx +++ b/packages/pie/src/PieCanvas.tsx @@ -90,8 +90,8 @@ const PieCanvas = ({ innerRadius: innerRadiusRatio = defaultProps.innerRadius, cornerRadius = defaultProps.cornerRadius, - width = 0, - height = 0, + width, + height, margin: partialMargin, pixelRatio = 1, diff --git a/packages/pie/src/types.ts b/packages/pie/src/types.ts index cddf76226..929eb3ed4 100644 --- a/packages/pie/src/types.ts +++ b/packages/pie/src/types.ts @@ -74,7 +74,7 @@ export type PieCustomLayer = React.FC> export type PieLayer = PieLayerId | PieCustomLayer -export type CommonPieProps = Dimensions & { +export type CommonPieProps = { id: string | DatumIdAccessorFunction value: string | DatumValueAccessorFunction valueFormat?: string | ValueFormatter @@ -130,6 +130,7 @@ export type PieHandlers = { } export type PieSvgProps = DataProps & + Dimensions & Partial> & SvgDefsAndFill> & PieHandlers & { @@ -137,6 +138,7 @@ export type PieSvgProps = DataProps & } export type CompletePieSvgProps = DataProps & + Dimensions & CommonPieProps & SvgDefsAndFill> & PieHandlers & { @@ -144,12 +146,14 @@ export type CompletePieSvgProps = DataProps & } export type PieCanvasProps = DataProps & + Dimensions & Partial> & Pick, 'onClick' | 'onMouseMove'> & { pixelRatio?: number } export type CompletePieCanvasProps = DataProps & + Dimensions & CommonPieProps & Pick, 'onClick' | 'onMouseMove'> & { pixelRatio: number From 597b6a91655454e99d3240747176cf6da3d54d05 Mon Sep 17 00:00:00 2001 From: Neil Kistner Date: Mon, 9 Nov 2020 16:55:08 -0600 Subject: [PATCH 18/18] fix(bullet): make dimensions required props --- packages/bullet/src/Bullet.tsx | 2 +- packages/bullet/src/types.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/bullet/src/Bullet.tsx b/packages/bullet/src/Bullet.tsx index 265fb7091..58d054480 100644 --- a/packages/bullet/src/Bullet.tsx +++ b/packages/bullet/src/Bullet.tsx @@ -47,7 +47,7 @@ export const Bullet = (props: BulletSvgProps) => { onMarkerClick, role, - } = { height: 0, width: 0, ...defaultProps, ...props } + } = { ...defaultProps, ...props } const { margin, innerWidth, innerHeight } = useDimensions(width, height, partialMargin) diff --git a/packages/bullet/src/types.ts b/packages/bullet/src/types.ts index 3cab41746..5899ad3a9 100644 --- a/packages/bullet/src/types.ts +++ b/packages/bullet/src/types.ts @@ -78,6 +78,7 @@ export type BulletHandlers = { } export type BulletSvgProps = Partial & + Dimensions & BulletHandlers & ModernMotionProps & { data: Datum[]