Browse Source

Re-style Chart tooltip, retrieve its data correctly

master
meriadec 7 years ago
parent
commit
09d001798a
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 32
      src/components/BalanceSummary/index.js
  2. 2
      src/components/DashboardPage/AccountCard.js
  3. 95
      src/components/base/Chart/Tooltip.js
  4. 35
      src/components/base/Chart/handleMouseEvents.js
  5. 2
      src/components/base/Chart/helpers.js
  6. 5
      src/components/base/Chart/index.js
  7. 4
      src/components/base/Chart/refreshDraw.js
  8. 15
      src/components/base/Chart/refreshNodes.js
  9. 36
      src/components/base/Chart/stories.js

32
src/components/BalanceSummary/index.js

@ -35,7 +35,8 @@ const BalanceSummary = ({
renderHeader,
selectedTime,
}: Props) => {
const unit = getFiatCurrencyByTicker(counterValue).units[0]
const currency = getFiatCurrencyByTicker(counterValue)
const account = accounts.length === 1 ? accounts[0] : null
return (
<Card p={0} py={6}>
<CalculateBalance
@ -61,21 +62,28 @@ const BalanceSummary = ({
<Box ff="Open Sans" fontSize={4} color="graphite" pt={6}>
<Chart
id={chartId}
account={account}
color={chartColor}
data={balanceHistory}
height={250}
unit={unit}
currency={currency}
tickXScale={selectedTime}
renderTooltip={d =>
isAvailable ? (
<FormattedVal
alwaysShowSign={false}
color="white"
showCode
fiat={counterValue}
val={d.value}
/>
) : null
renderTooltip={
isAvailable && !account
? d => (
<Fragment>
<FormattedVal
alwaysShowSign={false}
fontSize={5}
color="dark"
showCode
fiat={counterValue}
val={d.value}
/>
<Box mt="auto">{d.date.toISOString().substr(0, 10)}</Box>
</Fragment>
)
: null
}
/>
</Box>

2
src/components/DashboardPage/AccountCard.js

@ -88,7 +88,7 @@ const AccountCard = ({
hideAxis
isInteractive={false}
id={`account-chart-${account.id}`}
unit={account.unit}
account={account}
/>
</Box>
)}

95
src/components/base/Chart/Tooltip.js

@ -1,47 +1,37 @@
// @flow
import React from 'react'
import React, { Fragment } from 'react'
import styled from 'styled-components'
import type { Unit } from '@ledgerhq/live-common/lib/types'
import type { Account } from '@ledgerhq/live-common/lib/types'
import { colors as themeColors } from 'styles/theme'
import { TooltipContainer } from 'components/base/Tooltip'
import CounterValue from 'components/CounterValue'
import FormattedVal from 'components/base/FormattedVal'
import Box from 'components/base/Box'
import type { Item } from './types'
/**
* we use inline style for more perfs, as tooltip may re-render numerous times
*/
const Arrow = () => (
<svg
style={{
display: 'block',
position: 'absolute',
left: '50%',
bottom: 0,
marginBottom: -10,
transform: 'translate(-50%, 0)',
}}
viewBox="0 0 14 6.2"
width={16}
height={16}
>
<path fill={themeColors.dark} d="m14 0-5.5 5.6c-0.8 0.8-2 0.8-2.8 0l-5.7-5.6" />
</svg>
)
const Container = styled(Box).attrs({
px: 4,
py: 3,
align: 'center',
})`
background: white;
border: 1px solid #d8d8d8;
border-radius: 4px;
width: 150px;
height: 90px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.03);
`
const Tooltip = ({
d,
item,
renderTooltip,
fiat,
unit,
account,
}: {
d: Item,
item: Item,
renderTooltip?: Function,
fiat?: string,
unit?: Unit,
account: Account,
}) => (
<div style={{ position: 'relative' }}>
<div
@ -51,32 +41,37 @@ const Tooltip = ({
left: 0,
transform: `translate3d(-50%, 0, 0)`,
whiteSpace: 'nowrap',
marginBottom: 5,
marginBottom: -5,
}}
>
<TooltipContainer style={{ textAlign: 'center' }}>
<Container style={{ textAlign: 'center' }}>
{renderTooltip ? (
renderTooltip(d)
renderTooltip(item)
) : (
<FormattedVal
alwaysShowSign={false}
color="white"
showCode
fiat={fiat}
unit={unit}
val={d.value}
/>
<Fragment>
<FormattedVal
color="dark"
fontSize={5}
alwaysShowSign={false}
showCode
unit={account.currency.units[0]}
val={item.value}
/>
<CounterValue
account={account}
currency={account.currency}
value={item.value}
disableRounding
showCode
/>
<Box ff="Open Sans|Regular" color="grey" fontSize={3} mt="auto">
{item.date.toISOString().substr(0, 10)}
</Box>
</Fragment>
)}
</TooltipContainer>
<Arrow />
</Container>
</div>
</div>
)
Tooltip.defaultProps = {
renderTooltip: undefined,
fiat: undefined,
unit: undefined,
}
export default Tooltip

35
src/components/base/Chart/handleMouseEvents.js

@ -29,7 +29,7 @@ export default function handleMouseEvents({
renderTooltip?: Function,
}) {
const { MARGINS, HEIGHT, WIDTH, NODES, DATA, x, y } = ctx
const { hideAxis, unit } = props
const { account } = props
const bisectDate = d3.bisector(d => d.parsedDate).left
@ -65,21 +65,15 @@ export default function handleMouseEvents({
NODES.tooltip
.style('transition', '100ms cubic-bezier(.61,1,.53,1) opacity')
.style('opacity', 1)
.style('transform', `translate3d(${MARGINS.left + x(d.parsedDate)}px, ${y(d.value)}px, 0)`)
.style('transform', `translate3d(${MARGINS.left + x(d.parsedDate)}px, 0, 0)`)
NODES.focus.style('opacity', 1)
if (!hideAxis) {
NODES.xBar.style('opacity', 1)
NODES.yBar.style('opacity', 1)
}
NODES.xBar.style('opacity', 1)
}
function mouseOut() {
NODES.tooltip.style('opacity', 0).style('transition', '100ms linear opacity')
NODES.focus.style('opacity', 0)
if (!hideAxis) {
NODES.xBar.style('opacity', 0)
NODES.yBar.style('opacity', 0)
}
NODES.xBar.style('opacity', 0)
}
function mouseMove() {
@ -97,24 +91,17 @@ export default function handleMouseEvents({
renderToString(
<Provider store={createStore({})}>
<ThemeProvider theme={theme}>
<Tooltip unit={unit} renderTooltip={renderTooltip} d={d.ref} />
<Tooltip account={account} renderTooltip={renderTooltip} item={d.ref} />
</ThemeProvider>
</Provider>,
),
)
.style('transform', `translate3d(${MARGINS.left + x(d.parsedDate)}px, ${y(d.value)}px, 0)`)
if (!hideAxis) {
NODES.xBar
.attr('x1', x(d.parsedDate))
.attr('x2', x(d.parsedDate))
.attr('y1', HEIGHT)
.attr('y2', y(d.value))
NODES.yBar
.attr('x1', 0)
.attr('x2', x(d.parsedDate))
.attr('y1', y(d.value))
.attr('y2', y(d.value))
}
.style('transform', `translate3d(${MARGINS.left + x(d.parsedDate)}px, 0, 0)`)
NODES.xBar
.attr('x1', x(d.parsedDate))
.attr('x2', x(d.parsedDate))
.attr('y1', 0)
.attr('y2', HEIGHT)
}
return node

2
src/components/base/Chart/helpers.js

@ -16,7 +16,7 @@ export function generateColors(color) {
focus: color,
gradientStart: cColor.fade(0.7),
gradientStop: cColor.fade(1),
focusBar: cColor.fade(0.5),
focusBar: '#d8d8d8',
}
}

5
src/components/base/Chart/index.js

@ -37,7 +37,7 @@ import React, { PureComponent } from 'react'
import * as d3 from 'd3'
import noop from 'lodash/noop'
import type { Unit } from '@ledgerhq/live-common/lib/types'
import type { Account } from '@ledgerhq/live-common/lib/types'
import refreshNodes from './refreshNodes'
import refreshDraw from './refreshDraw'
@ -48,7 +48,7 @@ import type { Data } from './types'
export type Props = {
data: Data, // eslint-disable-line react/no-unused-prop-types
unit?: Unit, // eslint-disable-line react/no-unused-prop-types
account?: Account, // eslint-disable-line react/no-unused-prop-types
id?: string, // eslint-disable-line react/no-unused-prop-types
height?: number,
@ -69,7 +69,6 @@ class Chart extends PureComponent<Props> {
id: 'chart',
isInteractive: true,
tickXScale: 'month',
unit: undefined,
}
componentDidMount() {

4
src/components/base/Chart/refreshDraw.js

@ -65,9 +65,8 @@ export default function refreshDraw({ ctx, props }: { ctx: CTX, props: Props })
if (isInteractive) {
// Update focus bar colors
NODES.xBar.attr('stroke', COLORS.focusBar)
NODES.yBar.attr('stroke', COLORS.focusBar)
// Update dot color
NODES.focus.attr('fill', COLORS.focus)
NODES.focus.attr('stroke', COLORS.focus)
}
// Update gradient color
NODES.gradientStart.attr('stop-color', COLORS.gradientStart)
@ -82,7 +81,6 @@ export default function refreshDraw({ ctx, props }: { ctx: CTX, props: Props })
NODES.focus.style('opacity', 0)
NODES.tooltip.style('opacity', 0)
NODES.xBar.style('opacity', 0)
NODES.yBar.style('opacity', 0)
}
// Draw axis

15
src/components/base/Chart/refreshNodes.js

@ -30,16 +30,7 @@ export default function refreshNodes({ ctx, node, props }: { ctx: CTX, node: any
NODES.wrapper
.append('line')
.attr('stroke', COLORS.focusBar)
.attr('stroke-width', '1px')
.attr('stroke-dasharray', '3, 2'),
)
ensure({ onlyIf: isInteractive, NODES, key: 'yBar' }, () =>
NODES.wrapper
.append('line')
.attr('stroke', COLORS.focusBar)
.attr('stroke-width', '1px')
.attr('stroke-dasharray', '3, 2'),
.attr('stroke-width', '1px'),
)
// Gradient
@ -98,7 +89,9 @@ export default function refreshNodes({ ctx, node, props }: { ctx: CTX, node: any
NODES.wrapper
.append('g')
.append('circle')
.attr('fill', COLORS.focus)
.attr('fill', 'white')
.attr('stroke', COLORS.focus)
.attr('stroke-width', 2)
.attr('r', 4),
)

36
src/components/base/Chart/stories.js

@ -9,11 +9,15 @@ import { boolean, number } from '@storybook/addon-knobs'
import { color } from '@storybook/addon-knobs/react'
import Chart from 'components/base/Chart'
import Box from 'components/base/Box'
const stories = storiesOf('Components/base', module)
const data = generateRandomData(365)
const unit = getCryptoCurrencyById('bitcoin').units[0]
const currency = getCryptoCurrencyById('bitcoin')
const fakeAccount = {
currency,
}
type State = {
start: number,
@ -32,23 +36,31 @@ class Wrapper extends Component<any, State> {
const { start, stop } = this.state
return (
<Fragment>
<input type="range" value={start} onChange={this.handleChange('start')} min={0} max={365} />
<input
type="range"
value={stop}
style={{ marginLeft: 10 }}
onChange={this.handleChange('stop')}
min={0}
max={365}
/>
<Box mb={8} horizontal>
<input
type="range"
value={start}
onChange={this.handleChange('start')}
min={0}
max={365}
/>
<input
type="range"
value={stop}
style={{ marginLeft: 10 }}
onChange={this.handleChange('stop')}
min={0}
max={365}
/>
</Box>
<Chart
isInteractive={boolean('isInteractive', true)}
hideAxis={boolean('hideAxis', false)}
hideAxis={boolean('hideAxis', true)}
color={color('color', '#5f8ced')}
data={data.slice(start, stop)}
height={number('height', 300)}
unit={unit}
account={fakeAccount}
/>
</Fragment>
)

Loading…
Cancel
Save