Browse Source

Integrate new Chart in BalanceSummary & AccountCard

master
meriadec 7 years ago
parent
commit
8e6a141614
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 27
      src/components/BalanceSummary/index.js
  2. 9
      src/components/DashboardPage/AccountCard.js
  3. 23
      src/components/base/NewChart/Tooltip.js
  4. 13
      src/components/base/NewChart/handleMouseEvents.js
  5. 15
      src/components/base/NewChart/helpers.js
  6. 41
      src/components/base/NewChart/index.js
  7. 18
      src/components/base/NewChart/refreshDraw.js
  8. 6
      src/components/base/NewChart/refreshNodes.js
  9. 2
      src/helpers/balance.js

27
src/components/BalanceSummary/index.js

@ -7,9 +7,7 @@ import { formatShort, getFiatUnit } from '@ledgerhq/currencies'
import type { Accounts } from 'types/common'
import { space } from 'styles/theme'
import { AreaChart } from 'components/base/Chart'
import Chart from 'components/base/NewChart'
import Box, { Card } from 'components/base/Box'
import CalculateBalance from 'components/CalculateBalance'
import FormattedVal from 'components/base/FormattedVal'
@ -76,33 +74,24 @@ const BalanceSummary = ({
})}
</Box>
)}
<Box ff="Open Sans" fontSize={4} color="graphite">
<AreaChart
<Box ff="Open Sans" fontSize={4} color="graphite" pt={6}>
<Chart
id={chartId}
color={chartColor}
data={allBalances}
height={250}
id={chartId}
padding={{
top: space[6],
bottom: space[6],
left: space[6] * 2,
right: space[6],
}}
strokeWidth={2}
renderLabels={d => d.y}
nbTicksX={getTickCountX(selectedTime)}
renderTickX={renderTickX(selectedTime)}
renderTickY={t => formatShort(unit, t)}
renderTooltip={d => (
<FormattedVal
alwaysShowSign={false}
color="white"
showCode
fiat={counterValue}
val={d}
val={d.value}
/>
)}
renderTickX={renderTickX(selectedTime)}
renderTickY={t => formatShort(unit, t)}
tickCountX={getTickCountX(selectedTime)}
tickCountY={4}
/>
</Box>
</Fragment>

9
src/components/DashboardPage/AccountCard.js

@ -5,7 +5,7 @@ import { getIconByCoinType } from '@ledgerhq/currencies/react'
import type { Account } from 'types/common'
import { SimpleAreaChart } from 'components/base/Chart'
import Chart from 'components/base/NewChart'
import Bar from 'components/base/Bar'
import Box, { Card } from 'components/base/Box'
import CalculateBalance from 'components/CalculateBalance'
@ -84,14 +84,13 @@ const AccountCard = ({
/>
</Box>
</Box>
<SimpleAreaChart
<Chart
data={allBalances}
color={account.currency.color}
height={52}
hideAxis
interactive={false}
id={`account-chart-${account.id}`}
linearGradient={[[5, 0.2], [100, 0]]}
simple
strokeWidth={1.5}
/>
</Box>
)}

23
src/components/base/NewChart/Tooltip.js

@ -1,6 +1,6 @@
// @flow
import React from 'react'
import React, { Fragment } from 'react'
import { colors as themeColors } from 'styles/theme'
import { TooltipContainer } from 'components/base/Tooltip'
@ -8,10 +8,7 @@ import { TooltipContainer } from 'components/base/Tooltip'
import type { Item } from './types'
/**
* styled-components is not run on those components, because tooltip is
* rendered as a string on d3 updates
*
* so, we use inline style.
* we use inline style for more perfs, as tooltip may re-render numerous times
*/
const Arrow = () => (
@ -32,7 +29,7 @@ const Arrow = () => (
</svg>
)
export default ({ d }: { d: Item }) => (
const Tooltip = ({ d, renderTooltip }: { d: Item, renderTooltip?: Function }) => (
<div style={{ position: 'relative' }}>
<div
style={{
@ -45,10 +42,16 @@ export default ({ d }: { d: Item }) => (
}}
>
<TooltipContainer style={{ textAlign: 'center' }}>
{renderTooltip ? (
renderTooltip(d)
) : (
<Fragment>
<div style={{ fontSize: 14 }}>
<b>{Math.round(d.value)}</b>
</div>
{d.date}
<span>{d.date}</span>
</Fragment>
)}
</TooltipContainer>
<div style={{ background: 'red' }}>
<Arrow />
@ -56,3 +59,9 @@ export default ({ d }: { d: Item }) => (
</div>
</div>
)
Tooltip.defaultProps = {
renderTooltip: undefined,
}
export default Tooltip

13
src/components/base/NewChart/handleMouseEvents.js

@ -3,6 +3,9 @@
import React from 'react'
import * as d3 from 'd3'
import { renderToString } from 'react-dom/server'
import { ThemeProvider } from 'styled-components'
import theme from 'styles/theme'
import type { Props } from '.'
import type { CTX } from './types'
@ -14,11 +17,13 @@ export default function handleMouseEvents({
props,
shouldTooltipUpdate,
onTooltipUpdate,
renderTooltip,
}: {
ctx: CTX,
props: Props,
shouldTooltipUpdate: Function,
onTooltipUpdate: Function,
renderTooltip?: Function,
}) {
const { MARGINS, HEIGHT, WIDTH, NODES, DATA, x, y } = ctx
const { hideAxis } = props
@ -85,7 +90,13 @@ export default function handleMouseEvents({
onTooltipUpdate(d)
NODES.focus.attr('transform', `translate(${x(d.parsedDate)},${y(d.value)})`)
NODES.tooltip
.html(renderToString(<Tooltip d={d.ref} />))
.html(
renderToString(
<ThemeProvider theme={theme}>
<Tooltip renderTooltip={renderTooltip} d={d.ref} />
</ThemeProvider>,
),
)
.style('transform', `translate3d(${MARGINS.left + x(d.parsedDate)}px, ${y(d.value)}px, 0)`)
if (!hideAxis) {
NODES.xBar

15
src/components/base/NewChart/helpers.js

@ -21,12 +21,19 @@ export function generateColors(color) {
}
export function generateMargins(hideAxis) {
return {
const margins = {
top: hideAxis ? 5 : 10,
bottom: hideAxis ? 5 : 40,
right: hideAxis ? 5 : 10,
left: hideAxis ? 5 : 70,
bottom: hideAxis ? 5 : 30,
right: hideAxis ? 5 : 40,
left: hideAxis ? 5 : 80,
}
// FIXME: Forced to "use" margins here to prevent babel/uglify to believe
// there is a constant variable re-assignment. I don't get it, but it
// works, so, eh.
void margins
return margins
}
export function observeResize(node, cb) {

41
src/components/base/NewChart/index.js

@ -51,6 +51,11 @@ export type Props = {
interactive?: boolean, // eslint-disable-line react/no-unused-prop-types
height: number,
dateFormat?: string, // eslint-disable-line react/no-unused-prop-types
id?: string, // eslint-disable-line react/no-unused-prop-types
nbTicksX: number, // eslint-disable-line react/no-unused-prop-types
renderTooltip?: Function, // eslint-disable-line react/no-unused-prop-types
renderTickX?: Function, // eslint-disable-line react/no-unused-prop-types
renderTickY?: Function, // eslint-disable-line react/no-unused-prop-types
}
class Chart extends PureComponent<Props> {
@ -60,6 +65,8 @@ class Chart extends PureComponent<Props> {
interactive: true,
height: 400,
dateFormat: '%Y-%m-%d',
id: 'chart',
nbTicksX: 5,
}
componentDidMount() {
@ -103,43 +110,36 @@ class Chart extends PureComponent<Props> {
this.refreshChart = prevProps => {
const { _node: node, props } = this
const { data: raw, color, dateFormat, height, hideAxis, interactive } = props
let { COLORS, MARGINS } = ctx
const { data: raw, color, dateFormat, height, hideAxis, interactive, renderTooltip } = props
const DATA = enrichData(raw, d3.timeParse(dateFormat))
ctx.DATA = DATA
ctx.DATA = enrichData(raw, d3.timeParse(dateFormat))
// Detect what needs to be updated
const INVALIDATED = {
ctx.INVALIDATED = {
color: firstRender || (prevProps && color !== prevProps.color),
margin: firstRender || (prevProps && hideAxis !== prevProps.hideAxis),
}
firstRender = false
ctx.INVALIDATED = INVALIDATED
// Reset color if needed
if (INVALIDATED.color) {
COLORS = generateColors(color)
ctx.COLORS = COLORS
if (ctx.INVALIDATED.color) {
ctx.COLORS = generateColors(color)
}
// Reset margins if needed
if (INVALIDATED.margin) {
MARGINS = generateMargins(hideAxis)
ctx.MARGINS = MARGINS
if (ctx.INVALIDATED.margin) {
ctx.MARGINS = generateMargins(hideAxis)
}
// Derived draw variables
const HEIGHT = Math.max(0, height - MARGINS.top - MARGINS.bottom)
const WIDTH = Math.max(0, this._width - MARGINS.left - MARGINS.right)
ctx.HEIGHT = HEIGHT
ctx.WIDTH = WIDTH
ctx.HEIGHT = Math.max(0, height - ctx.MARGINS.top - ctx.MARGINS.bottom)
ctx.WIDTH = Math.max(0, this._width - ctx.MARGINS.left - ctx.MARGINS.right)
// Scales and areas
const x = d3.scaleTime().range([0, WIDTH])
const y = d3.scaleLinear().range([HEIGHT, 0])
x.domain(d3.extent(DATA, d => d.parsedDate))
y.domain([0, d3.max(DATA, d => d.value)])
const x = d3.scaleTime().range([0, ctx.WIDTH])
const y = d3.scaleLinear().range([ctx.HEIGHT, 0])
x.domain(d3.extent(ctx.DATA, d => d.parsedDate))
y.domain([0, d3.max(ctx.DATA, d => d.value)])
ctx.x = x
ctx.y = y
@ -160,6 +160,7 @@ class Chart extends PureComponent<Props> {
props,
shouldTooltipUpdate: d => d !== lastDisplayedTooltip,
onTooltipUpdate: d => (lastDisplayedTooltip = d),
renderTooltip,
})
}
}

18
src/components/base/NewChart/refreshDraw.js

@ -9,7 +9,7 @@ import type { CTX } from './types'
export default function refreshDraw({ ctx, props }: { ctx: CTX, props: Props }) {
const { NODES, WIDTH, HEIGHT, MARGINS, COLORS, INVALIDATED, DATA, x, y } = ctx
const { hideAxis, interactive } = props
const { hideAxis, interactive, renderTickX, renderTickY, nbTicksX } = props
const area = d3
.area()
@ -61,8 +61,18 @@ export default function refreshDraw({ ctx, props }: { ctx: CTX, props: Props })
// Draw axis
if (!hideAxis) {
NODES.axisLeft.call(d3.axisLeft(y).ticks(3))
NODES.axisBot.call(d3.axisBottom(x).ticks(5))
NODES.axisLeft.call(
d3
.axisLeft(y)
.ticks(3)
.tickFormat(val => (renderTickY ? renderTickY(val) : val)),
)
NODES.axisBot.call(
d3
.axisBottom(x)
.ticks(nbTicksX)
.tickFormat(val => (renderTickX ? renderTickX(val) : val)),
)
stylizeAxis(NODES.axisLeft)
stylizeAxis(NODES.axisBot)
}
@ -99,7 +109,7 @@ function stylizeAxis(axis) {
axis.selectAll('path').attr('stroke', 'none')
axis
.selectAll('text')
.attr('stroke', themeColors.smoke)
.attr('stroke', themeColors.grey)
.style('font-size', '12px')
.style('font-family', 'Open Sans')
.style('font-weight', 300)

6
src/components/base/NewChart/refreshNodes.js

@ -10,7 +10,7 @@ const debug = d('Chart')
export default function refreshNodes({ ctx, node, props }: { ctx: CTX, node: any, props: Props }) {
const { NODES, COLORS } = ctx
const { hideAxis, interactive } = props
const { hideAxis, interactive, id } = props
// Container
@ -47,7 +47,7 @@ export default function refreshNodes({ ctx, node, props }: { ctx: CTX, node: any
ensure({ NODES, key: 'gradient' }, () =>
NODES.defs
.append('linearGradient')
.attr('id', 'gradient')
.attr('id', `gradient-${id || ''}`)
.attr('x1', '0%')
.attr('x2', '0%')
.attr('y1', '0%')
@ -69,7 +69,7 @@ export default function refreshNodes({ ctx, node, props }: { ctx: CTX, node: any
)
ensure({ NODES, key: 'fillArea' }, () =>
NODES.wrapper.append('path').attr('fill', 'url(#gradient)'),
NODES.wrapper.append('path').attr('fill', `url(#gradient-${id || ''})`),
)
// Line

2
src/helpers/balance.js

@ -140,7 +140,7 @@ export default function calculateBalance(props: CalculateBalance) {
accounts: props.accounts,
counterValues: props.counterValues,
interval,
}).map(e => ({ name: e.date, value: e.balance }))
}).map(e => ({ date: e.date, value: e.balance }))
const firstNonEmptyDay = find(allBalances, e => e.value)
const refBalance = firstNonEmptyDay ? firstNonEmptyDay.value : 0

Loading…
Cancel
Save