From 8e6a141614a510ec840c902cf28d7f59434c0330 Mon Sep 17 00:00:00 2001 From: meriadec Date: Mon, 12 Mar 2018 13:45:36 +0100 Subject: [PATCH] Integrate new Chart in BalanceSummary & AccountCard --- src/components/BalanceSummary/index.js | 27 ++++-------- src/components/DashboardPage/AccountCard.js | 9 ++-- src/components/base/NewChart/Tooltip.js | 29 ++++++++----- .../base/NewChart/handleMouseEvents.js | 13 +++++- src/components/base/NewChart/helpers.js | 15 +++++-- src/components/base/NewChart/index.js | 41 ++++++++++--------- src/components/base/NewChart/refreshDraw.js | 18 ++++++-- src/components/base/NewChart/refreshNodes.js | 6 +-- src/helpers/balance.js | 2 +- 9 files changed, 93 insertions(+), 67 deletions(-) diff --git a/src/components/BalanceSummary/index.js b/src/components/BalanceSummary/index.js index 236d5084..543f4481 100644 --- a/src/components/BalanceSummary/index.js +++ b/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 = ({ })} )} - - + d.y} + nbTicksX={getTickCountX(selectedTime)} + renderTickX={renderTickX(selectedTime)} + renderTickY={t => formatShort(unit, t)} renderTooltip={d => ( )} - renderTickX={renderTickX(selectedTime)} - renderTickY={t => formatShort(unit, t)} - tickCountX={getTickCountX(selectedTime)} - tickCountY={4} /> diff --git a/src/components/DashboardPage/AccountCard.js b/src/components/DashboardPage/AccountCard.js index fcb9bf89..d7be5911 100644 --- a/src/components/DashboardPage/AccountCard.js +++ b/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 = ({ /> - )} diff --git a/src/components/base/NewChart/Tooltip.js b/src/components/base/NewChart/Tooltip.js index b1be1fa3..ace1533c 100644 --- a/src/components/base/NewChart/Tooltip.js +++ b/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 = () => ( ) -export default ({ d }: { d: Item }) => ( +const Tooltip = ({ d, renderTooltip }: { d: Item, renderTooltip?: Function }) => (
( }} > -
- {Math.round(d.value)} -
- {d.date} + {renderTooltip ? ( + renderTooltip(d) + ) : ( + +
+ {Math.round(d.value)} +
+ {d.date} +
+ )}
@@ -56,3 +59,9 @@ export default ({ d }: { d: Item }) => (
) + +Tooltip.defaultProps = { + renderTooltip: undefined, +} + +export default Tooltip diff --git a/src/components/base/NewChart/handleMouseEvents.js b/src/components/base/NewChart/handleMouseEvents.js index 81278931..656a404e 100644 --- a/src/components/base/NewChart/handleMouseEvents.js +++ b/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()) + .html( + renderToString( + + + , + ), + ) .style('transform', `translate3d(${MARGINS.left + x(d.parsedDate)}px, ${y(d.value)}px, 0)`) if (!hideAxis) { NODES.xBar diff --git a/src/components/base/NewChart/helpers.js b/src/components/base/NewChart/helpers.js index ca98019a..0f2559ef 100644 --- a/src/components/base/NewChart/helpers.js +++ b/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) { diff --git a/src/components/base/NewChart/index.js b/src/components/base/NewChart/index.js index 879a31f0..2f19ac8f 100644 --- a/src/components/base/NewChart/index.js +++ b/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 { @@ -60,6 +65,8 @@ class Chart extends PureComponent { interactive: true, height: 400, dateFormat: '%Y-%m-%d', + id: 'chart', + nbTicksX: 5, } componentDidMount() { @@ -103,43 +110,36 @@ class Chart extends PureComponent { 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, shouldTooltipUpdate: d => d !== lastDisplayedTooltip, onTooltipUpdate: d => (lastDisplayedTooltip = d), + renderTooltip, }) } } diff --git a/src/components/base/NewChart/refreshDraw.js b/src/components/base/NewChart/refreshDraw.js index c9acc0fb..a44bba93 100644 --- a/src/components/base/NewChart/refreshDraw.js +++ b/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) diff --git a/src/components/base/NewChart/refreshNodes.js b/src/components/base/NewChart/refreshNodes.js index 3abd612c..856bba10 100644 --- a/src/components/base/NewChart/refreshNodes.js +++ b/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 diff --git a/src/helpers/balance.js b/src/helpers/balance.js index 77d4e034..c51279b6 100644 --- a/src/helpers/balance.js +++ b/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