From 90006b67491125f2a4fbb7ec9909b3fc79c04ae3 Mon Sep 17 00:00:00 2001 From: plouc Date: Wed, 29 Dec 2021 08:48:17 +0900 Subject: [PATCH] feat(bump): add tests for the Bump chart --- packages/bump/_old_index.d.ts | 201 ------------------------- packages/bump/src/bump/Line.tsx | 2 + packages/bump/src/bump/LineTooltip.tsx | 11 +- packages/bump/src/bump/LinesLabels.tsx | 3 +- packages/bump/src/bump/Point.tsx | 1 + packages/bump/src/bump/defaults.ts | 6 +- packages/bump/src/bump/hooks.ts | 2 +- packages/bump/src/bump/types.ts | 10 +- packages/bump/tests/.eslintrc.yml | 2 + packages/bump/tests/Bump.test.tsx | 169 +++++++++++++++++++++ website/src/pages/bump/index.tsx | 10 +- 11 files changed, 200 insertions(+), 217 deletions(-) delete mode 100644 packages/bump/_old_index.d.ts create mode 100644 packages/bump/tests/.eslintrc.yml create mode 100644 packages/bump/tests/Bump.test.tsx diff --git a/packages/bump/_old_index.d.ts b/packages/bump/_old_index.d.ts deleted file mode 100644 index 1d2ad5e5d1..0000000000 --- a/packages/bump/_old_index.d.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { AxisProps } from '@nivo/axes' -import { Component, MouseEvent } from 'react' -import { Dimensions, Box, Theme, MotionProps, SvgDefsAndFill, CssMixBlendMode } from '@nivo/core' -import { OrdinalColorScaleConfig, InheritedColorConfig } from '@nivo/colors' - -declare module '@nivo/bump' { - type SerieDerivedProp = (serie: Serie) => T - - export interface BumpInputDatum { - x?: string | number | null - y?: string | number | null - [key: string]: any - } - - export interface BumpInputSerie { - id: string - data: BumpInputDatum[] - [key: string]: any - } - - export interface BumpPoint { - id: string - serie: BumpInputSerie - data: BumpInputDatum - x: number | null - y: number | null - } - - export interface BumpComputedSerie extends BumpInputSerie { - points: BumpPoint[] - linePoints: [number, number][] - } - - export interface BumpComputedPoint extends BumpPoint { - serie: BumpComputedSerie - serieId: string - isActive: boolean - isInactive: boolean - } - - export type BumpLabelFunction = (serie: BumpInputSerie) => string - export type BumpLabel = false | string | BumpLabelFunction - - export type BumpMouseHandler = ( - serie: BumpInputSerie, - event: MouseEvent - ) => void - - export interface BumpProps { - data: BumpInputSerie[] - - margin?: Box - - align?: AreaBumpAlign - interpolation?: AreaBumpInterpolation - xOuterPadding?: number - yOuterPadding?: number - xPadding?: number - - theme?: Theme - colors?: OrdinalColorScaleConfig - - startLabel?: BumpLabel - startLabelPadding?: number - startLabelTextColor?: InheritedColorConfig - endLabel?: BumpLabel - endLabelPadding?: number - endLabelTextColor?: InheritedColorConfig - - pointSize?: number - activePointSize?: number - inactivePointSize?: number - pointColor?: InheritedColorConfig - pointBorderWidth?: number - activePointBorderWidth?: number - inactivePointBorderWidth?: number - pointBorderColor?: InheritedColorConfig - - lineWidth?: number - activeLineWidth?: number - inactiveLineWidth?: number - inactiveOpacity?: number - - enableGridX?: boolean - enableGridY?: boolean - axisTop?: AxisProps | null - axisRight?: AxisProps | null - axisBottom?: AxisProps | null - axisLeft?: AxisProps | null - - isInteractive?: boolean - onMouseEnter?: BumpMouseHandler - onMouseMove?: BumpMouseHandler - onMouseLeave?: BumpMouseHandler - onClick?: BumpMouseHandler - tooltip?: React.FC<{ serie: BumpComputedSerie }> - role?: string - } - - export type BumpSvgProps = BumpProps & MotionProps - export class Bump extends Component {} - export class ResponsiveBump extends Component {} - - export interface AreaBumpInputDatum { - x: string | number - y: number - [key: string]: any - } - - export interface AreaBumpInputSerie { - id: string - data: AreaBumpInputDatum[] - [key: string]: any - } - - export interface AreaBumpAreaPoint { - x: number - y0: number - y1: number - } - - export interface AreaBumpPoint { - x: number - y: number - height: number - data: AreaBumpInputDatum - } - - export interface AreaBumpComputedSerie extends AreaBumpInputSerie { - color: string - style: { - fillOpacity: number - borderWidth: number - borderColor: string - borderOpacity: number - } - points: AreaBumpPoint[] - areaPoints: AreaBumpAreaPoint[] - } - - export type AreaBumpAlign = 'start' | 'middle' | 'end' - export type AreaBumpInterpolation = 'smooth' | 'linear' - - export type AreaBumpLayerType = 'grid' | 'axes' | 'labels' | 'areas' - - export type AreaBumpLabelFunction = (serie: AreaBumpComputedSerie) => string - export type AreaBumpLabel = false | string | AreaBumpLabelFunction - - export type AreaBumpMouseHandler = ( - serie: AreaBumpComputedSerie, - event: MouseEvent - ) => void - - export interface AreaBumpProps { - data: AreaBumpInputSerie[] - - margin?: Box - - align?: AreaBumpAlign - interpolation?: AreaBumpInterpolation - spacing?: number - xPadding?: number - - theme?: Theme - colors?: OrdinalColorScaleConfig - blendMode?: CssMixBlendMode - fillOpacity?: number | SerieDerivedProp - activeFillOpacity?: number | SerieDerivedProp - inactiveFillOpacity?: number | SerieDerivedProp - borderWidth?: number | SerieDerivedProp - activeBorderWidth?: number | SerieDerivedProp - inactiveBorderWidth?: number | SerieDerivedProp - borderColor?: InheritedColorConfig - borderOpacity?: number | SerieDerivedProp - activeBorderOpacity?: number | SerieDerivedProp - inactiveBorderOpacity?: number | SerieDerivedProp - - startLabel?: AreaBumpLabel - startLabelPadding?: number - startLabelTextColor?: InheritedColorConfig - endLabel?: AreaBumpLabel - endLabelPadding?: number - endLabelTextColor?: InheritedColorConfig - - enableGridX?: boolean - axisTop?: AxisProps - axisBottom?: AxisProps - - isInteractive?: boolean - onMouseEnter?: AreaBumpMouseHandler - onMouseMove?: AreaBumpMouseHandler - onMouseLeave?: AreaBumpMouseHandler - onClick?: AreaBumpMouseHandler - tooltip?: React.FC<{ serie: AreaBumpComputedSerie }> - role?: string - } - - export type AreaBumpSvgProps = AreaBumpProps & MotionProps & SvgDefsAndFill - export class AreaBump extends Component {} - export class ResponsiveAreaBump extends Component {} -} diff --git a/packages/bump/src/bump/Line.tsx b/packages/bump/src/bump/Line.tsx index 2d6c94f8df..3167d2359b 100644 --- a/packages/bump/src/bump/Line.tsx +++ b/packages/bump/src/bump/Line.tsx @@ -60,6 +60,7 @@ export const Line = ({ return ( <> ({ /> {isInteractive && ( { serie: BumpComputedSerie } -export const LineTooltip = ({ serie }: LineTooltipProps) => { - return -} +export const LineTooltip = ({ serie }: LineTooltipProps) => ( + +) diff --git a/packages/bump/src/bump/LinesLabels.tsx b/packages/bump/src/bump/LinesLabels.tsx index 6a88896912..7e644e0a9a 100644 --- a/packages/bump/src/bump/LinesLabels.tsx +++ b/packages/bump/src/bump/LinesLabels.tsx @@ -6,7 +6,7 @@ import { useBumpSeriesLabels } from './hooks' interface LineLabelsProps { series: BumpComputedSerie[] - getLabel: BumpLabel + getLabel: BumpLabel position: 'start' | 'end' padding: number color: InheritedColorConfig> @@ -52,6 +52,7 @@ export const LinesLabels = ({ return ( ({ point }: PointProps) => { return ( Math.max(v, 0))} diff --git a/packages/bump/src/bump/defaults.ts b/packages/bump/src/bump/defaults.ts index bcf95105f0..0f144f3c6f 100644 --- a/packages/bump/src/bump/defaults.ts +++ b/packages/bump/src/bump/defaults.ts @@ -1,7 +1,7 @@ import { ModernMotionProps } from '@nivo/core' import { LineTooltip } from './LineTooltip' import { Point } from './Point' -import { BumpCommonProps, BumpSvgProps } from './types' +import { BumpCommonProps, BumpPointComponent } from './types' const commonDefaultProps: Omit< BumpCommonProps, @@ -32,7 +32,7 @@ const commonDefaultProps: Omit< startLabel: false, startLabelPadding: 16, startLabelTextColor: { from: 'color' }, - endLabel: 'id', + endLabel: true, endLabelPadding: 16, endLabelTextColor: { from: 'color' }, @@ -59,7 +59,7 @@ const commonDefaultProps: Omit< } export const bumpSvgDefaultProps: typeof commonDefaultProps & { - pointComponent: BumpSvgProps['pointComponent'] + pointComponent: BumpPointComponent animate: boolean motionConfig: ModernMotionProps['motionConfig'] } = { diff --git a/packages/bump/src/bump/hooks.ts b/packages/bump/src/bump/hooks.ts index 018e91c549..9f607f6224 100644 --- a/packages/bump/src/bump/hooks.ts +++ b/packages/bump/src/bump/hooks.ts @@ -375,7 +375,7 @@ export const useBumpSeriesLabels = ({ position: 'start' | 'end' padding: number color: InheritedColorConfig> - getLabel: BumpLabel + getLabel: BumpLabel }) => { const theme = useTheme() const getColor = useInheritedColor(color, theme) diff --git a/packages/bump/src/bump/types.ts b/packages/bump/src/bump/types.ts index c174813235..9d1a509cbc 100644 --- a/packages/bump/src/bump/types.ts +++ b/packages/bump/src/bump/types.ts @@ -1,5 +1,5 @@ import { FunctionComponent, MouseEvent } from 'react' -import { Theme, Box, Dimensions, ModernMotionProps, PropertyAccessor } from '@nivo/core' +import { Theme, Box, Dimensions, ModernMotionProps } from '@nivo/core' import { OrdinalColorScaleConfig, InheritedColorConfig } from '@nivo/colors' import { AxisProps } from '@nivo/axes' @@ -56,7 +56,7 @@ export type BumpDataProps = { export type BumpInterpolation = 'smooth' | 'linear' -export type BumpLabel = PropertyAccessor | false +export type BumpLabel = ((serie: BumpComputedSerie) => string) | boolean export interface BumpLabelData { serie: BumpComputedSerie id: BumpSerie['id'] @@ -94,10 +94,10 @@ export type BumpCommonProps = { activeOpacity: number inactiveOpacity: number - startLabel: BumpLabel + startLabel: BumpLabel startLabelPadding: number startLabelTextColor: InheritedColorConfig> - endLabel: BumpLabel + endLabel: BumpLabel endLabelPadding: number endLabelTextColor: InheritedColorConfig> @@ -133,6 +133,6 @@ export type BumpCommonProps = { export type BumpSvgProps = Partial> & BumpDataProps & { - pointComponent: BumpPointComponent + pointComponent?: BumpPointComponent } & Dimensions & ModernMotionProps diff --git a/packages/bump/tests/.eslintrc.yml b/packages/bump/tests/.eslintrc.yml new file mode 100644 index 0000000000..2f8de9aea2 --- /dev/null +++ b/packages/bump/tests/.eslintrc.yml @@ -0,0 +1,2 @@ +env: + jest: true diff --git a/packages/bump/tests/Bump.test.tsx b/packages/bump/tests/Bump.test.tsx new file mode 100644 index 0000000000..6552c49acd --- /dev/null +++ b/packages/bump/tests/Bump.test.tsx @@ -0,0 +1,169 @@ +import { mount } from 'enzyme' +// @ts-ignore +import { Bump, BumpSvgProps } from '../src' + +interface Datum { + x: number + y: number +} + +const sampleData: BumpSvgProps['data'] = [ + { + id: 'A', + data: [ + { + x: 2000, + y: 9, + }, + { + x: 2001, + y: 9, + }, + { + x: 2002, + y: 2, + }, + { + x: 2003, + y: 4, + }, + ], + }, + { + id: 'B', + data: [ + { + x: 2000, + y: 8, + }, + { + x: 2001, + y: 3, + }, + { + x: 2002, + y: 1, + }, + { + x: 2003, + y: 7, + }, + ], + }, + { + id: 'C', + data: [ + { + x: 2000, + y: 12, + }, + { + x: 2001, + y: 4, + }, + { + x: 2002, + y: 5, + }, + { + x: 2003, + y: 6, + }, + ], + }, +] + +const baseProps: BumpSvgProps = { + width: 800, + height: 600, + data: sampleData, + animate: false, +} + +it('should render a basic bump chart', () => { + const wrapper = mount( {...baseProps} />) + + const lineA = wrapper.find(`path[data-testid='line.A']`) + expect(lineA.exists()).toBeTruthy() + + const lineB = wrapper.find(`path[data-testid='line.B']`) + expect(lineB.exists()).toBeTruthy() + + const lineC = wrapper.find(`path[data-testid='line.C']`) + expect(lineC.exists()).toBeTruthy() +}) + +describe('style', () => { + it('custom colors array', () => { + const colors = ['rgba(255, 0, 0, 1)', 'rgba(0, 255, 0, 1)', 'rgba(0, 0, 255, 1)'] + const wrapper = mount( {...baseProps} colors={colors} />) + + expect(wrapper.find(`path[data-testid='line.A']`).prop('stroke')).toEqual(colors[0]) + expect(wrapper.find(`circle[data-testid='point.A.2000']`).prop('fill')).toEqual(colors[0]) + expect(wrapper.find(`circle[data-testid='point.A.2001']`).prop('fill')).toEqual(colors[0]) + expect(wrapper.find(`circle[data-testid='point.A.2002']`).prop('fill')).toEqual(colors[0]) + expect(wrapper.find(`circle[data-testid='point.A.2003']`).prop('fill')).toEqual(colors[0]) + + expect(wrapper.find(`path[data-testid='line.B']`).prop('stroke')).toEqual(colors[1]) + expect(wrapper.find(`circle[data-testid='point.B.2000']`).prop('fill')).toEqual(colors[1]) + expect(wrapper.find(`circle[data-testid='point.B.2001']`).prop('fill')).toEqual(colors[1]) + expect(wrapper.find(`circle[data-testid='point.B.2002']`).prop('fill')).toEqual(colors[1]) + expect(wrapper.find(`circle[data-testid='point.B.2003']`).prop('fill')).toEqual(colors[1]) + + expect(wrapper.find(`path[data-testid='line.C']`).prop('stroke')).toEqual(colors[2]) + expect(wrapper.find(`circle[data-testid='point.C.2000']`).prop('fill')).toEqual(colors[2]) + expect(wrapper.find(`circle[data-testid='point.C.2001']`).prop('fill')).toEqual(colors[2]) + expect(wrapper.find(`circle[data-testid='point.C.2002']`).prop('fill')).toEqual(colors[2]) + expect(wrapper.find(`circle[data-testid='point.C.2003']`).prop('fill')).toEqual(colors[2]) + }) +}) + +describe('labels', () => { + it('default end labels', () => { + const wrapper = mount( {...baseProps} />) + + const endLabelA = wrapper.find(`text[data-testid='label.end.A']`) + expect(endLabelA.exists()).toBeTruthy() + expect(endLabelA.text()).toEqual('A') + expect(endLabelA.prop('textAnchor')).toEqual('start') + + const endLabelB = wrapper.find(`text[data-testid='label.end.B']`) + expect(endLabelB.exists()).toBeTruthy() + expect(endLabelB.text()).toEqual('B') + expect(endLabelB.prop('textAnchor')).toEqual('start') + + const endLabelC = wrapper.find(`text[data-testid='label.end.C']`) + expect(endLabelC.exists()).toBeTruthy() + expect(endLabelC.text()).toEqual('C') + expect(endLabelC.prop('textAnchor')).toEqual('start') + }) + + it('customize end labels', () => { + const wrapper = mount( + {...baseProps} endLabel={serie => `Serie ${serie.id}`} /> + ) + + expect(wrapper.find(`text[data-testid='label.end.A']`).text()).toEqual('Serie A') + expect(wrapper.find(`text[data-testid='label.end.B']`).text()).toEqual('Serie B') + expect(wrapper.find(`text[data-testid='label.end.C']`).text()).toEqual('Serie C') + }) + + it('start labels', () => { + const wrapper = mount( {...baseProps} startLabel />) + + const startLabelA = wrapper.find(`text[data-testid='label.start.A']`) + expect(startLabelA.exists()).toBeTruthy() + expect(startLabelA.text()).toEqual('A') + expect(startLabelA.prop('textAnchor')).toEqual('end') + + const startLabelB = wrapper.find(`text[data-testid='label.start.B']`) + expect(startLabelB.exists()).toBeTruthy() + expect(startLabelB.text()).toEqual('B') + expect(startLabelB.prop('textAnchor')).toEqual('end') + + const startLabelC = wrapper.find(`text[data-testid='label.start.C']`) + expect(startLabelC.exists()).toBeTruthy() + expect(startLabelC.text()).toEqual('C') + expect(startLabelC.prop('textAnchor')).toEqual('end') + }) +}) diff --git a/website/src/pages/bump/index.tsx b/website/src/pages/bump/index.tsx index c53ddedbbd..2bdc7ff82f 100644 --- a/website/src/pages/bump/index.tsx +++ b/website/src/pages/bump/index.tsx @@ -2,7 +2,7 @@ import React from 'react' import range from 'lodash/range' import shuffle from 'lodash/shuffle' import { graphql, useStaticQuery } from 'gatsby' -import { ResponsiveBump, bumpSvgDefaultProps as defaults } from '@nivo/bump' +import { ResponsiveBump, bumpSvgDefaultProps as defaults, BumpCommonProps } from '@nivo/bump' import { ComponentTemplate } from '../../components/components/ComponentTemplate' import meta from '../../data/components/bump/meta.yml' import { groups } from '../../data/components/bump/props' @@ -37,7 +37,11 @@ const generateData = () => { return series } -const initialProperties = { +type UnmappedProps = Omit, 'whatever'> + +type Props = UnmappedProps + +const initialProperties: UnmappedProps = { margin: { top: 40, right: 100, @@ -137,7 +141,7 @@ const Bump = () => { `) return ( - name="Bump" meta={meta.Bump} icon="chord"