Skip to content

Commit

Permalink
feat(arcs): improve labels handling
Browse files Browse the repository at this point in the history
  • Loading branch information
plouc committed Dec 18, 2020
1 parent 90958bd commit c1bfd51
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 116 deletions.
12 changes: 3 additions & 9 deletions packages/arcs/src/arc_labels/ArcLabelsLayer.tsx
@@ -1,10 +1,5 @@
import React, { useMemo } from 'react'
import {
// @ts-ignore
getLabelGenerator,
radiansToDegrees,
useTheme,
} from '@nivo/core'
import { PropertyAccessor, usePropertyAccessor, radiansToDegrees, useTheme } from '@nivo/core'
import { useInheritedColor } from '@nivo/colors'
import { useArcCentersTransition } from '../centers'
import { ArcTransitionMode } from '../arcTransitionMode'
Expand All @@ -19,8 +14,7 @@ export type ArcLabelComponent<Datum extends DatumWithArcAndColor> = (
interface ArcLabelsLayerProps<Datum extends DatumWithArcAndColor> {
center: [number, number]
data: Datum[]
// @todo add proper label accessor type
label: any
label: PropertyAccessor<Datum, string>
radiusOffset: ArcLabelsProps<Datum>['arcLabelsRadiusOffset']
skipAngle: ArcLabelsProps<Datum>['arcLabelsSkipAngle']
textColor: ArcLabelsProps<Datum>['arcLabelsTextColor']
Expand All @@ -38,7 +32,7 @@ export const ArcLabelsLayer = <Datum extends DatumWithArcAndColor>({
textColor,
component = ArcLabel,
}: ArcLabelsLayerProps<Datum>) => {
const getLabel = useMemo(() => getLabelGenerator(labelAccessor), [labelAccessor])
const getLabel = usePropertyAccessor<Datum, string>(labelAccessor)
const theme = useTheme()
const getTextColor = useInheritedColor<Datum>(textColor, theme)

Expand Down
5 changes: 2 additions & 3 deletions packages/arcs/src/arc_labels/props.ts
@@ -1,11 +1,10 @@
import { PropertyAccessor } from '@nivo/core'
import { InheritedColorConfig } from '@nivo/colors'
import { ArcLabelComponent } from './ArcLabelsLayer'
import { DatumWithArcAndColor } from '../types'

export interface ArcLabelsProps<Datum extends DatumWithArcAndColor> {
// @todo fix label accessor
// string | LabelAccessorFunction<Datum['data']>
arcLabel: any
arcLabel: PropertyAccessor<Datum, string>
arcLabelsRadiusOffset: number
arcLabelsSkipAngle: number
arcLabelsTextColor: InheritedColorConfig<Datum>
Expand Down
13 changes: 4 additions & 9 deletions packages/arcs/src/arc_labels/useArcLabels.ts
@@ -1,9 +1,5 @@
import { useCallback, useMemo } from 'react'
import {
// @ts-ignore
getLabelGenerator,
useTheme,
} from '@nivo/core'
import { useCallback } from 'react'
import { PropertyAccessor, usePropertyAccessor, useTheme } from '@nivo/core'
import { InheritedColorConfig, useInheritedColor } from '@nivo/colors'
import { DatumWithArcAndColor } from '../types'
import { useArcCenters, ArcCenter } from '../centers'
Expand All @@ -30,11 +26,10 @@ export const useArcLabels = <Datum extends DatumWithArcAndColor>({
data: Datum[]
offset?: number
skipAngle?: number
// @todo come up with proper typing for label accessors, probably in `core`
label: any
label: PropertyAccessor<Datum, string>
textColor: InheritedColorConfig<Datum>
}) => {
const getLabel = useMemo(() => getLabelGenerator(label), [label])
const getLabel = usePropertyAccessor<Datum, string>(label)

const theme = useTheme()
const getTextColor = useInheritedColor<Datum>(textColor, theme)
Expand Down
@@ -1,7 +1,7 @@
import { useMemo } from 'react'
import { SpringValue, useTransition, to } from 'react-spring'
import { line } from 'd3-shape'
import { useMotionConfig, useTheme } from '@nivo/core'
import { useMotionConfig, useTheme, PropertyAccessor } from '@nivo/core'
import { InheritedColorConfig, useInheritedColor } from '@nivo/colors'
import { DatumWithArcAndColor, Point } from '../types'
import { computeArcLink } from '../links'
Expand Down Expand Up @@ -141,8 +141,7 @@ export const useArcLinkLabelsTransition = <Datum extends DatumWithArcAndColor>({
straightLength: number
skipAngle?: number
textOffset: number
// @todo come up with proper typing for label accessors, probably in `core`
label: any
label: PropertyAccessor<Datum, string>
linkColor: InheritedColorConfig<Datum>
textColor: InheritedColorConfig<Datum>
}) => {
Expand Down
9 changes: 4 additions & 5 deletions packages/arcs/src/links.ts
Expand Up @@ -2,8 +2,8 @@ import { useCallback, useMemo } from 'react'
import {
positionFromAngle,
radiansToDegrees,
// @ts-ignore
getLabelGenerator,
PropertyAccessor,
usePropertyAccessor,
useTheme,
} from '@nivo/core'
import { InheritedColorConfig, useInheritedColor } from '@nivo/colors'
Expand Down Expand Up @@ -151,12 +151,11 @@ export const useArcLinkLabels = <Datum extends DatumWithArcAndColor>({
diagonalLength: number
straightLength: number
textOffset: number
// @todo come up with proper typing for label accessors, probably in `core`
label: any
label: PropertyAccessor<Datum, string>
linkColor: InheritedColorConfig<Datum>
textColor: InheritedColorConfig<Datum>
}) => {
const getLabel = useMemo(() => getLabelGenerator(label), [label])
const getLabel = usePropertyAccessor<Datum, string>(label)

const theme = useTheme()
const getLinkColor = useInheritedColor<Datum>(linkColor, theme)
Expand Down
170 changes: 85 additions & 85 deletions packages/pie/tests/Pie.test.tsx
Expand Up @@ -42,20 +42,20 @@ describe('Pie', () => {
<Pie width={400} height={400} data={sampleData} animate={false} />
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)

expect(slices.at(0).prop('datum').id).toEqual('A')
expect(slices.at(0).prop('datum').value).toEqual(30)
expect(slices.at(0).prop('datum').formattedValue).toEqual(30)
expect(arcs.at(0).prop('datum').id).toEqual('A')
expect(arcs.at(0).prop('datum').value).toEqual(30)
expect(arcs.at(0).prop('datum').formattedValue).toEqual('30')

expect(slices.at(1).prop('datum').id).toEqual('B')
expect(slices.at(1).prop('datum').value).toEqual(20)
expect(slices.at(1).prop('datum').formattedValue).toEqual(20)
expect(arcs.at(1).prop('datum').id).toEqual('B')
expect(arcs.at(1).prop('datum').value).toEqual(20)
expect(arcs.at(1).prop('datum').formattedValue).toEqual('20')

expect(slices.at(2).prop('datum').id).toEqual('C')
expect(slices.at(2).prop('datum').value).toEqual(50)
expect(slices.at(2).prop('datum').formattedValue).toEqual(50)
expect(arcs.at(2).prop('datum').id).toEqual('C')
expect(arcs.at(2).prop('datum').value).toEqual(50)
expect(arcs.at(2).prop('datum').formattedValue).toEqual('50')
})

it('should use custom id and value accessors expressed as path', () => {
Expand All @@ -70,20 +70,20 @@ describe('Pie', () => {
/>
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)

expect(slices.at(0).prop('datum').id).toEqual('A')
expect(slices.at(0).prop('datum').value).toEqual(30)
expect(slices.at(0).prop('datum').formattedValue).toEqual(30)
expect(arcs.at(0).prop('datum').id).toEqual('A')
expect(arcs.at(0).prop('datum').value).toEqual(30)
expect(arcs.at(0).prop('datum').formattedValue).toEqual('30')

expect(slices.at(1).prop('datum').id).toEqual('B')
expect(slices.at(1).prop('datum').value).toEqual(20)
expect(slices.at(1).prop('datum').formattedValue).toEqual(20)
expect(arcs.at(1).prop('datum').id).toEqual('B')
expect(arcs.at(1).prop('datum').value).toEqual(20)
expect(arcs.at(1).prop('datum').formattedValue).toEqual('20')

expect(slices.at(2).prop('datum').id).toEqual('C')
expect(slices.at(2).prop('datum').value).toEqual(50)
expect(slices.at(2).prop('datum').formattedValue).toEqual(50)
expect(arcs.at(2).prop('datum').id).toEqual('C')
expect(arcs.at(2).prop('datum').value).toEqual(50)
expect(arcs.at(2).prop('datum').formattedValue).toEqual('50')
})

it('should use custom id and value accessors expressed as functions', () => {
Expand All @@ -98,20 +98,20 @@ describe('Pie', () => {
/>
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)

expect(slices.at(0).prop('datum').id).toEqual('A')
expect(slices.at(0).prop('datum').value).toEqual(30)
expect(slices.at(0).prop('datum').formattedValue).toEqual(30)
expect(arcs.at(0).prop('datum').id).toEqual('A')
expect(arcs.at(0).prop('datum').value).toEqual(30)
expect(arcs.at(0).prop('datum').formattedValue).toEqual('30')

expect(slices.at(1).prop('datum').id).toEqual('B')
expect(slices.at(1).prop('datum').value).toEqual(20)
expect(slices.at(1).prop('datum').formattedValue).toEqual(20)
expect(arcs.at(1).prop('datum').id).toEqual('B')
expect(arcs.at(1).prop('datum').value).toEqual(20)
expect(arcs.at(1).prop('datum').formattedValue).toEqual('20')

expect(slices.at(2).prop('datum').id).toEqual('C')
expect(slices.at(2).prop('datum').value).toEqual(50)
expect(slices.at(2).prop('datum').formattedValue).toEqual(50)
expect(arcs.at(2).prop('datum').id).toEqual('C')
expect(arcs.at(2).prop('datum').value).toEqual(50)
expect(arcs.at(2).prop('datum').formattedValue).toEqual('50')
})

it('should support custom value formatting', () => {
Expand All @@ -125,38 +125,38 @@ describe('Pie', () => {
/>
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)

expect(slices.at(0).prop('datum').id).toEqual('A')
expect(slices.at(0).prop('datum').value).toEqual(30)
expect(slices.at(0).prop('datum').formattedValue).toEqual('$30.00')
expect(arcs.at(0).prop('datum').id).toEqual('A')
expect(arcs.at(0).prop('datum').value).toEqual(30)
expect(arcs.at(0).prop('datum').formattedValue).toEqual('$30.00')

expect(slices.at(1).prop('datum').id).toEqual('B')
expect(slices.at(1).prop('datum').value).toEqual(20)
expect(slices.at(1).prop('datum').formattedValue).toEqual('$20.00')
expect(arcs.at(1).prop('datum').id).toEqual('B')
expect(arcs.at(1).prop('datum').value).toEqual(20)
expect(arcs.at(1).prop('datum').formattedValue).toEqual('$20.00')

expect(slices.at(2).prop('datum').id).toEqual('C')
expect(slices.at(2).prop('datum').value).toEqual(50)
expect(slices.at(2).prop('datum').formattedValue).toEqual('$50.00')
expect(arcs.at(2).prop('datum').id).toEqual('C')
expect(arcs.at(2).prop('datum').value).toEqual(50)
expect(arcs.at(2).prop('datum').formattedValue).toEqual('$50.00')
})

it('should support sorting data by value', () => {
const wrapper = mount(
<Pie width={400} height={400} data={sampleData} sortByValue animate={false} />
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)

const slice30 = slices.at(0)
const slice20 = slices.at(1)
const slice50 = slices.at(2)
const arc30 = arcs.at(0)
const arc20 = arcs.at(1)
const arc50 = arcs.at(2)

expect(slice50.prop('datum').arc.startAngle).toEqual(0)
expect(slice30.prop('datum').arc.startAngle).toBeGreaterThan(0)
expect(slice20.prop('datum').arc.startAngle).toBeGreaterThan(
slice30.prop('datum').arc.startAngle
expect(arc50.prop('datum').arc.startAngle).toEqual(0)
expect(arc30.prop('datum').arc.startAngle).toBeGreaterThan(0)
expect(arc20.prop('datum').arc.startAngle).toBeGreaterThan(
arc30.prop('datum').arc.startAngle
)
})
})
Expand All @@ -168,20 +168,20 @@ describe('Pie', () => {
)

// we can use a slice to check computed radii
const slice = wrapper.find('ArcShape').at(0)
expect(slice.prop('datum').arc.innerRadius).toEqual(100)
expect(slice.prop('datum').arc.outerRadius).toEqual(200)
const arc = wrapper.find('ArcShape').at(0)
expect(arc.prop('datum').arc.innerRadius).toEqual(100)
expect(arc.prop('datum').arc.outerRadius).toEqual(200)
})

it('should support padAngle', () => {
const wrapper = mount(
<Pie width={400} height={400} data={sampleData} padAngle={10} animate={false} />
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
slices.forEach(slice => {
expect(radiansToDegrees(slice.prop('datum').arc.padAngle)).toEqual(10)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)
arcs.forEach(arc => {
expect(radiansToDegrees(arc.prop('datum').arc.padAngle)).toEqual(10)
})
})

Expand Down Expand Up @@ -217,10 +217,10 @@ describe('Pie', () => {
/>
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
expect(radiansToDegrees(slices.at(0).prop('datum').arc.startAngle)).toEqual(90)
expect(radiansToDegrees(slices.at(2).prop('datum').arc.endAngle)).toEqual(180)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)
expect(radiansToDegrees(arcs.at(0).prop('datum').arc.startAngle)).toEqual(90)
expect(radiansToDegrees(arcs.at(2).prop('datum').arc.endAngle)).toEqual(180)
})

it('should support optimizing space usage via the fit property', () => {
Expand All @@ -238,9 +238,9 @@ describe('Pie', () => {
)

// we can use a slice to check computed radii
const slice = wrapper.find('ArcShape').at(0)
expect(slice.prop('datum').arc.innerRadius).toEqual(200)
expect(slice.prop('datum').arc.outerRadius).toEqual(400)
const arc = wrapper.find('ArcShape').at(0)
expect(arc.prop('datum').arc.innerRadius).toEqual(200)
expect(arc.prop('datum').arc.outerRadius).toEqual(400)
})
})

Expand All @@ -256,17 +256,17 @@ describe('Pie', () => {
/>
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)

expect(slices.at(0).prop('datum').id).toEqual('A')
expect(slices.at(0).prop('datum').color).toEqual('#7fc97f')
expect(arcs.at(0).prop('datum').id).toEqual('A')
expect(arcs.at(0).prop('datum').color).toEqual('#7fc97f')

expect(slices.at(1).prop('datum').id).toEqual('B')
expect(slices.at(1).prop('datum').color).toEqual('#beaed4')
expect(arcs.at(1).prop('datum').id).toEqual('B')
expect(arcs.at(1).prop('datum').color).toEqual('#beaed4')

expect(slices.at(2).prop('datum').id).toEqual('C')
expect(slices.at(2).prop('datum').color).toEqual('#fdc086')
expect(arcs.at(2).prop('datum').id).toEqual('C')
expect(arcs.at(2).prop('datum').color).toEqual('#fdc086')
})

it('should allow to use colors from data using a path', () => {
Expand All @@ -280,17 +280,17 @@ describe('Pie', () => {
/>
)

const slices = wrapper.find('ArcShape')
expect(slices).toHaveLength(sampleData.length)
const arcs = wrapper.find('ArcShape')
expect(arcs).toHaveLength(sampleData.length)

expect(slices.at(0).prop('datum').id).toEqual('A')
expect(slices.at(0).prop('datum').color).toEqual('#ff5500')
expect(arcs.at(0).prop('datum').id).toEqual('A')
expect(arcs.at(0).prop('datum').color).toEqual('#ff5500')

expect(slices.at(1).prop('datum').id).toEqual('B')
expect(slices.at(1).prop('datum').color).toEqual('#ffdd00')
expect(arcs.at(1).prop('datum').id).toEqual('B')
expect(arcs.at(1).prop('datum').color).toEqual('#ffdd00')

expect(slices.at(2).prop('datum').id).toEqual('C')
expect(slices.at(2).prop('datum').color).toEqual('#99cc44')
expect(arcs.at(2).prop('datum').id).toEqual('C')
expect(arcs.at(2).prop('datum').color).toEqual('#99cc44')
})

it('should allow to use colors from data using a function', () => {
Expand Down Expand Up @@ -408,7 +408,7 @@ describe('Pie', () => {
})

it('should allow to customize the label component', () => {
const CustomArcLabel = () => null
const CustomArcLabel = () => <span />
const wrapper = mount(
<Pie
width={400}
Expand All @@ -423,7 +423,7 @@ describe('Pie', () => {
expect(labels).toHaveLength(sampleData.length)

sampleData.forEach((datum, index) => {
expect(labels.at(index).prop('label')).toEqual(datum.value)
expect(labels.at(index).prop('label')).toEqual(`${datum.value}`)
})
})
})
Expand Down

0 comments on commit c1bfd51

Please sign in to comment.