From cd32c58fb4bcd731d8a83d354c9b01a38c8df219 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Wed, 10 Oct 2018 13:36:38 -0230 Subject: Complete integration of gas chart with redux. --- .../gas-price-chart/gas-price-chart.component.js | 94 ++++++++++++++++++++-- .../tests/gas-price-chart.component.test.js | 11 ++- 2 files changed, 97 insertions(+), 8 deletions(-) (limited to 'ui/app/components/gas-customization/gas-price-chart') diff --git a/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js b/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js index 85893f771..69bbd12f6 100644 --- a/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js +++ b/ui/app/components/gas-customization/gas-price-chart/gas-price-chart.component.js @@ -35,6 +35,43 @@ function appendOrUpdateCircle ({ circle, data, itemIndex, cx, cy, cssId, appendO } } +function setSelectedCircle ({ chart, gasPrices, currentPrice, chartXStart, chartWidth }) { + const numberOfValues = chart.internal.data.xs.data1.length + const closestLowerValueIndex = gasPrices.findIndex((e, i, a) => { + return e <= currentPrice && a[i + 1] >= currentPrice + }) + const closestHigherValueIndex = gasPrices.findIndex((e, i, a) => { + return e > currentPrice + }) + const closestHigherValue = gasPrices[closestHigherValueIndex] + const closestLowerValue = gasPrices[closestLowerValueIndex] + + if (closestHigherValue && closestLowerValue) { + const closestLowerCircle = d3.select(`.c3-circle-${closestLowerValueIndex}`) + const closestHigherCircle = d3.select(`.c3-circle-${closestHigherValueIndex}`) + const { x: lowerX, y: lowerY } = closestLowerCircle.node().getBoundingClientRect() + const { x: higherX, y: higherY } = closestHigherCircle.node().getBoundingClientRect() + const currentX = lowerX + (higherX - lowerX) * (currentPrice - closestLowerValue) / (closestHigherValue - closestLowerValue) + const slope = (higherY - lowerY) / (higherX - lowerX) + const newTimeEstimate = -1 * (slope * (higherX - currentX) - higherY) + chart.internal.selectPointB({ + x: currentX, + value: newTimeEstimate, + id: 'data1', + index: numberOfValues, + name: 'data1', + }, numberOfValues) + } else { + const setCircle = d3.select('#set-circle') + if (!setCircle.empty()) { + setCircle.remove() + } + d3.select('.c3-tooltip-container').style('display', 'none !important') + chart.internal.hideXGridFocus() + return + } +} + export default class GasPriceChart extends Component { static contextTypes = { t: PropTypes.func, @@ -42,12 +79,15 @@ export default class GasPriceChart extends Component { static propTypes = { priceAndTimeEstimates: PropTypes.array, + currentPrice: PropTypes.number, + updateCustomGasPrice: PropTypes.func, } - renderChart (priceAndTimeEstimates) { + renderChart (currentPrice, priceAndTimeEstimates, updateCustomGasPrice) { const gasPrices = priceAndTimeEstimates.map(({ gasprice }) => gasprice) const gasPricesMax = gasPrices[gasPrices.length - 1] + 1 const estimatedTimes = priceAndTimeEstimates.map(({ expectedTime }) => expectedTime) + const estimatedTimesMax = estimatedTimes[0] const chart = c3.generate({ size: { @@ -187,6 +227,28 @@ export default class GasPriceChart extends Component { }) } + chart.internal.selectPointB = function (data, itemIndex = (data.index || 0)) { + const { x: chartXStart, y: chartYStart } = d3.select('.c3-areas-data1') + .node() + .getBoundingClientRect() + + d3.select('#set-circle').remove() + + const circle = this.main + .select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id)) + .selectAll('.' + 'c3-selected-circle' + '-' + itemIndex) + + appendOrUpdateCircle.bind(this)({ + circle, + data, + itemIndex, + cx: () => data.x - chartXStart + 11, + cy: () => data.value - chartYStart + 10, + cssId: 'set-circle', + appendOnly: true, + }) + } + chart.internal.overlayPoint = function (data, itemIndex) { const circle = this.main .select('.' + 'c3-selected-circles' + this.getTargetSelectorSuffix(data.id)) @@ -238,13 +300,13 @@ export default class GasPriceChart extends Component { setTimeout(function () { setTickPosition('y', 0, -5, 8) - setTickPosition('y', 1, -3) - setTickPosition('x', 0, 3, 20) - setTickPosition('x', 1, 3, -10) + setTickPosition('y', 1, -3, -5) + setTickPosition('x', 0, 3, 15) + setTickPosition('x', 1, 3, -8) // TODO: Confirm the below constants work with all data sets and screen sizes d3.select('.c3-axis-x-label').attr('transform', 'translate(0,-15)') - d3.select('.c3-axis-y-label').attr('transform', 'translate(32, 2) rotate(-90)') + d3.select('.c3-axis-y-label').attr('transform', 'translate(52, 2) rotate(-90)') d3.select('.c3-xgrid-focus line').attr('y2', 98) d3.select('.c3-chart').on('mouseout', () => { @@ -262,6 +324,7 @@ export default class GasPriceChart extends Component { const overlayedCircle = d3.select('#overlayed-circle') const numberOfValues = chart.internal.data.xs.data1.length const { x: circleX, y: circleY } = overlayedCircle.node().getBoundingClientRect() + const { x: xData } = overlayedCircle.datum() chart.internal.selectPoint({ x: circleX - chartXStart, value: circleY - 1.5, @@ -269,13 +332,15 @@ export default class GasPriceChart extends Component { index: numberOfValues, name: 'data1', }, numberOfValues) + updateCustomGasPrice(xData) }) + setSelectedCircle({ chart, gasPrices, currentPrice, chartXStart, chartWidth }) + d3.select('.c3-chart').on('mousemove', function () { const chartMouseXPos = d3.event.clientX - chartXStart const posPercentile = chartMouseXPos / chartWidth - const currentPosValue = (gasPrices[gasPrices.length - 1] - gasPrices[0]) * posPercentile + gasPrices[0] const closestLowerValueIndex = gasPrices.findIndex((e, i, a) => { return e <= currentPosValue && a[i + 1] >= currentPosValue @@ -326,11 +391,26 @@ export default class GasPriceChart extends Component { }) }, 0) + this.chart = chart + } + componentDidUpdate (prevProps) { + if (prevProps.currentPrice !== this.props.currentPrice) { + const chartRect = d3.select('.c3-areas-data1') + const { x: chartXStart, width: chartWidth } = chartRect.node().getBoundingClientRect() + setSelectedCircle({ + chart: this.chart, + currentPrice: this.props.currentPrice, + gasPrices: this.props.priceAndTimeEstimates.map(({ gasprice }) => gasprice), + chartXStart, + chartWidth, + }) + } } componentDidMount () { - this.renderChart(this.props.priceAndTimeEstimates) + const { currentPrice, priceAndTimeEstimates, updateCustomGasPrice } = this.props + this.renderChart(currentPrice, priceAndTimeEstimates, updateCustomGasPrice) } render () { diff --git a/ui/app/components/gas-customization/gas-price-chart/tests/gas-price-chart.component.test.js b/ui/app/components/gas-customization/gas-price-chart/tests/gas-price-chart.component.test.js index ae98659cc..48b8a6525 100644 --- a/ui/app/components/gas-customization/gas-price-chart/tests/gas-price-chart.component.test.js +++ b/ui/app/components/gas-customization/gas-price-chart/tests/gas-price-chart.component.test.js @@ -10,6 +10,9 @@ const mockSelectReturn = { node: () => ({ getBoundingClientRect: () => ({ x: 123, y: 321, width: 400 }), }), + empty: sinon.spy(), + remove: sinon.spy(), + style: sinon.spy(), select: d3.select, attr: sinon.spy(), on: sinon.spy(), @@ -17,11 +20,17 @@ const mockSelectReturn = { const GasPriceChart = proxyquire('../gas-price-chart.component.js', { 'c3': { - generate: function () { + generate: function ({ data: { columns } }) { return { internal: { showTooltip: () => {}, showXGridFocus: () => {}, + hideXGridFocus: () => {}, + data: { + xs: { + [columns[1][0]]: columns[1].slice(1), + }, + }, }, } }, -- cgit