Commit 49f1acb5 authored by Brendan Wong's avatar Brendan Wong Committed by GitHub

fix: displays price when not updated on TDP (#7068)

* Update PriceChart.tsx

* simplify deltaarrow function

* update text coloring

* increase gap a bit more

* rename tags

* restyling of information

* fix import issue

* Update src/components/Tokens/TokenDetails/PriceChart.tsx
Co-authored-by: default avatarJordan Frankfurt <jordanwfrankfurt@gmail.com>

* unit testing!

---------
Co-authored-by: default avatarJordan Frankfurt <jordanwfrankfurt@gmail.com>
parent a97005e2
import { TimePeriod } from 'graphql/data/util'
import { render } from 'test-utils/render'
import { PriceChart } from './PriceChart'
jest.mock('components/Charts/AnimatedInLineChart', () => ({
__esModule: true,
default: jest.fn(() => null),
}))
jest.mock('components/Charts/FadeInLineChart', () => ({
__esModule: true,
default: jest.fn(() => null),
}))
describe('PriceChart', () => {
it('renders correctly with all prices filled', () => {
const mockPrices = Array.from({ length: 13 }, (_, i) => ({
value: 1,
timestamp: i * 3600,
}))
const { asFragment } = render(
<PriceChart prices={mockPrices} width={780} height={436} timePeriod={TimePeriod.HOUR} />
)
expect(asFragment()).toMatchSnapshot()
expect(asFragment().textContent).toContain('$1.00')
expect(asFragment().textContent).toContain('0.00%')
})
it('renders correctly with some prices filled', () => {
const mockPrices = Array.from({ length: 13 }, (_, i) => ({
value: i < 10 ? 1 : 0,
timestamp: i * 3600,
}))
const { asFragment } = render(
<PriceChart prices={mockPrices} width={780} height={436} timePeriod={TimePeriod.HOUR} />
)
expect(asFragment()).toMatchSnapshot()
expect(asFragment().textContent).toContain('$1.00')
expect(asFragment().textContent).toContain('0.00%')
})
it('renders correctly with no prices filled', () => {
const { asFragment } = render(<PriceChart prices={[]} width={780} height={436} timePeriod={TimePeriod.HOUR} />)
expect(asFragment()).toMatchSnapshot()
expect(asFragment().textContent).toContain('Price Unavailable')
})
})
...@@ -6,12 +6,13 @@ import { GlyphCircle } from '@visx/glyph' ...@@ -6,12 +6,13 @@ import { GlyphCircle } from '@visx/glyph'
import { Line } from '@visx/shape' import { Line } from '@visx/shape'
import AnimatedInLineChart from 'components/Charts/AnimatedInLineChart' import AnimatedInLineChart from 'components/Charts/AnimatedInLineChart'
import FadedInLineChart from 'components/Charts/FadeInLineChart' import FadedInLineChart from 'components/Charts/FadeInLineChart'
import { MouseoverTooltip } from 'components/Tooltip'
import { bisect, curveCardinal, NumberValue, scaleLinear, timeDay, timeHour, timeMinute, timeMonth } from 'd3' import { bisect, curveCardinal, NumberValue, scaleLinear, timeDay, timeHour, timeMinute, timeMonth } from 'd3'
import { PricePoint } from 'graphql/data/util' import { PricePoint } from 'graphql/data/util'
import { TimePeriod } from 'graphql/data/util' import { TimePeriod } from 'graphql/data/util'
import { useActiveLocale } from 'hooks/useActiveLocale' import { useActiveLocale } from 'hooks/useActiveLocale'
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react' import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react'
import { ArrowDownRight, ArrowUpRight, TrendingUp } from 'react-feather' import { ArrowDownRight, ArrowUpRight, Info, TrendingUp } from 'react-feather'
import styled, { useTheme } from 'styled-components' import styled, { useTheme } from 'styled-components'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
import { textFadeIn } from 'theme/styles' import { textFadeIn } from 'theme/styles'
...@@ -41,18 +42,33 @@ const StyledDownArrow = styled(ArrowDownRight)` ...@@ -41,18 +42,33 @@ const StyledDownArrow = styled(ArrowDownRight)`
color: ${({ theme }) => theme.accentFailure}; color: ${({ theme }) => theme.accentFailure};
` `
const DefaultUpArrow = styled(ArrowUpRight)`
color: ${({ theme }) => theme.textTertiary};
`
const DefaultDownArrow = styled(ArrowDownRight)`
color: ${({ theme }) => theme.textTertiary};
`
function calculateDelta(start: number, current: number) { function calculateDelta(start: number, current: number) {
return (current / start - 1) * 100 return (current / start - 1) * 100
} }
export function getDeltaArrow(delta: number | null | undefined, iconSize = 20) { export function getDeltaArrow(delta: number | null | undefined, iconSize = 20, styled = true) {
// Null-check not including zero // Null-check not including zero
if (delta === null || delta === undefined) { if (delta === null || delta === undefined) {
return null return null
} else if (Math.sign(delta) < 0) { } else if (Math.sign(delta) < 0) {
return <StyledDownArrow size={iconSize} key="arrow-down" aria-label="down" /> return styled ? (
<StyledDownArrow size={iconSize} key="arrow-down" aria-label="down" />
) : (
<DefaultDownArrow size={iconSize} key="arrow-down" aria-label="down" />
)
} }
return <StyledUpArrow size={iconSize} key="arrow-up" aria-label="up" /> return styled ? (
<StyledUpArrow size={iconSize} key="arrow-up" aria-label="up" />
) : (
<DefaultUpArrow size={iconSize} key="arrow-up" aria-label="up" />
)
} }
export function formatDelta(delta: number | null | undefined) { export function formatDelta(delta: number | null | undefined) {
...@@ -84,6 +100,10 @@ const MissingPrice = styled(TokenPrice)` ...@@ -84,6 +100,10 @@ const MissingPrice = styled(TokenPrice)`
color: ${({ theme }) => theme.textTertiary}; color: ${({ theme }) => theme.textTertiary};
` `
const OutdatedContainer = styled.div`
color: ${({ theme }) => theme.textSecondary};
`
const DeltaContainer = styled.div` const DeltaContainer = styled.div`
height: 16px; height: 16px;
display: flex; display: flex;
...@@ -95,6 +115,13 @@ export const ArrowCell = styled.div` ...@@ -95,6 +115,13 @@ export const ArrowCell = styled.div`
display: flex; display: flex;
` `
const OutdatedPriceContainer = styled.div`
display: flex;
gap: 6px;
font-size: 24px;
line-height: 44px;
`
function fixChart(prices: PricePoint[] | undefined | null) { function fixChart(prices: PricePoint[] | undefined | null) {
if (!prices) return { prices: null, blanks: [] } if (!prices) return { prices: null, blanks: [] }
...@@ -148,6 +175,34 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod } ...@@ -148,6 +175,34 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
) )
) : null ) : null
const tooltipMessage = (
<>
<Trans>This price may not be up-to-date due to low trading volume.</Trans>
</>
)
//get the last non-zero price point
const lastPrice = useMemo(() => {
if (!prices) return DATA_EMPTY
for (let i = prices.length - 1; i >= 0; i--) {
if (prices[i].value !== 0) return prices[i]
}
return DATA_EMPTY
}, [prices])
//get the first non-zero price point
const firstPrice = useMemo(() => {
if (!prices) return DATA_EMPTY
for (let i = 0; i < prices.length; i++) {
if (prices[i].value !== 0) return prices[i]
}
return DATA_EMPTY
}, [prices])
const totalDelta = calculateDelta(firstPrice.value, lastPrice.value)
const formattedTotalDelta = formatDelta(totalDelta)
const defaultArrow = getDeltaArrow(totalDelta, 20, false)
// first price point on the x-axis of the current time period's chart // first price point on the x-axis of the current time period's chart
const startingPrice = originalPrices?.[0] ?? DATA_EMPTY const startingPrice = originalPrices?.[0] ?? DATA_EMPTY
// last price point on the x-axis of the current time period's chart // last price point on the x-axis of the current time period's chart
...@@ -302,6 +357,19 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod } ...@@ -302,6 +357,19 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
<ArrowCell>{arrow}</ArrowCell> <ArrowCell>{arrow}</ArrowCell>
</DeltaContainer> </DeltaContainer>
</> </>
) : lastPrice.value ? (
<OutdatedContainer>
<OutdatedPriceContainer>
<TokenPrice>{formatUSDPrice(lastPrice.value)}</TokenPrice>
<MouseoverTooltip text={tooltipMessage}>
<Info size={16} />
</MouseoverTooltip>
</OutdatedPriceContainer>
<DeltaContainer>
{formattedTotalDelta}
<ArrowCell>{defaultArrow}</ArrowCell>
</DeltaContainer>
</OutdatedContainer>
) : ( ) : (
<> <>
<MissingPrice>Price Unavailable</MissingPrice> <MissingPrice>Price Unavailable</MissingPrice>
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PriceChart renders correctly with all prices filled 1`] = `
<DocumentFragment>
.c4 {
color: #40B66B;
}
.c0 {
position: absolute;
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
-webkit-animation-duration: 250ms;
animation-duration: 250ms;
}
.c1 {
font-size: 36px;
line-height: 44px;
}
.c2 {
height: 16px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
margin-top: 4px;
}
.c3 {
padding-right: 3px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
<div
class="c0"
data-cy="chart-header"
>
<span
class="c1"
>
$1.00
</span>
<div
class="c2"
>
0.00%
<div
class="c3"
>
<svg
aria-label="up"
class="c4"
fill="none"
height="20"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="7"
x2="17"
y1="17"
y2="7"
/>
<polyline
points="7 7 17 7 17 17"
/>
</svg>
</div>
</div>
</div>
<svg
data-cy="price-chart"
height="392"
style="min-width: 100%;"
width="780"
>
<g
class="visx-group visx-axis visx-axis-bottom"
transform="translate(0, 391)"
>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="0"
y="18"
>
<tspan
dy="0em"
x="0"
>
0
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="90.27777777777777"
y="18"
>
<tspan
dy="0em"
x="90.27777777777777"
>
5,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="180.55555555555554"
y="18"
>
<tspan
dy="0em"
x="180.55555555555554"
>
10,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="270.8333333333333"
y="18"
>
<tspan
dy="0em"
x="270.8333333333333"
>
15,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="361.1111111111111"
y="18"
>
<tspan
dy="0em"
x="361.1111111111111"
>
20,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="451.3888888888889"
y="18"
>
<tspan
dy="0em"
x="451.3888888888889"
>
25,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="541.6666666666666"
y="18"
>
<tspan
dy="0em"
x="541.6666666666666"
>
30,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="631.9444444444445"
y="18"
>
<tspan
dy="0em"
x="631.9444444444445"
>
35,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="722.2222222222222"
y="18"
>
<tspan
dy="0em"
x="722.2222222222222"
>
40,000
</tspan>
</text>
</svg>
</g>
</g>
<rect
fill="transparent"
height="392"
width="780"
x="0"
y="0"
/>
</svg>
</DocumentFragment>
`;
exports[`PriceChart renders correctly with no prices filled 1`] = `
<DocumentFragment>
.c3 {
color: #0D111C;
}
.c0 {
position: absolute;
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
-webkit-animation-duration: 250ms;
animation-duration: 250ms;
}
.c1 {
font-size: 36px;
line-height: 44px;
}
.c2 {
font-size: 24px;
line-height: 44px;
color: #98A1C0;
}
<div
class="c0"
data-cy="chart-header"
>
<span
class="c1 c2"
>
Price Unavailable
</span>
<div
class="c3 css-4u0e4f"
style="color: rgb(152, 161, 192);"
>
Missing chart data
</div>
</div>
.c0 text {
font-size: 12px;
font-weight: 400;
}
<svg
class="c0"
data-cy="missing-chart"
height="392"
style="min-width: 100%;"
width="780"
>
<path
d="M 0 241 Q 104 171, 208 241 T 416 241
M 416 241 Q 520 171, 624 241 T 832 241"
fill="transparent"
stroke="#D2D9EE"
stroke-width="2"
/>
<text
fill="#98A1C0"
x="20"
y="377"
/>
</svg>
</DocumentFragment>
`;
exports[`PriceChart renders correctly with some prices filled 1`] = `
<DocumentFragment>
.c4 {
display: inline-block;
height: inherit;
}
.c7 {
color: #98A1C0;
}
.c0 {
position: absolute;
-webkit-animation: iAjNNh 125ms ease-in;
animation: iAjNNh 125ms ease-in;
-webkit-animation-duration: 250ms;
animation-duration: 250ms;
}
.c3 {
font-size: 36px;
line-height: 44px;
}
.c1 {
color: #7780A0;
}
.c5 {
height: 16px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
margin-top: 4px;
}
.c6 {
padding-right: 3px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.c2 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
gap: 6px;
font-size: 24px;
line-height: 44px;
}
<div
class="c0"
data-cy="chart-header"
>
<div
class="c1"
>
<div
class="c2"
>
<span
class="c3"
>
$1.00
</span>
<div
class="c4"
>
<div>
<svg
fill="none"
height="16"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<circle
cx="12"
cy="12"
r="10"
/>
<line
x1="12"
x2="12"
y1="16"
y2="12"
/>
<line
x1="12"
x2="12.01"
y1="8"
y2="8"
/>
</svg>
</div>
</div>
</div>
<div
class="c5"
>
0.00%
<div
class="c6"
>
<svg
aria-label="up"
class="c7"
fill="none"
height="20"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
viewBox="0 0 24 24"
width="20"
xmlns="http://www.w3.org/2000/svg"
>
<line
x1="7"
x2="17"
y1="17"
y2="7"
/>
<polyline
points="7 7 17 7 17 17"
/>
</svg>
</div>
</div>
</div>
</div>
<svg
data-cy="price-chart"
height="392"
style="min-width: 100%;"
width="780"
>
<g
class="visx-group visx-axis visx-axis-bottom"
transform="translate(0, 391)"
>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="0"
y="18"
>
<tspan
dy="0em"
x="0"
>
0
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="90.27777777777777"
y="18"
>
<tspan
dy="0em"
x="90.27777777777777"
>
5,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="180.55555555555554"
y="18"
>
<tspan
dy="0em"
x="180.55555555555554"
>
10,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="270.8333333333333"
y="18"
>
<tspan
dy="0em"
x="270.8333333333333"
>
15,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="361.1111111111111"
y="18"
>
<tspan
dy="0em"
x="361.1111111111111"
>
20,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="451.3888888888889"
y="18"
>
<tspan
dy="0em"
x="451.3888888888889"
>
25,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="541.6666666666666"
y="18"
>
<tspan
dy="0em"
x="541.6666666666666"
>
30,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="631.9444444444445"
y="18"
>
<tspan
dy="0em"
x="631.9444444444445"
>
35,000
</tspan>
</text>
</svg>
</g>
<g
class="visx-group visx-axis-tick"
transform="translate(0, 0)"
>
<svg
font-size="10"
style="overflow: visible;"
x="0"
y="0.25em"
>
<text
fill="#222"
font-family="Arial"
font-size="10"
text-anchor="middle"
transform=""
x="722.2222222222222"
y="18"
>
<tspan
dy="0em"
x="722.2222222222222"
>
40,000
</tspan>
</text>
</svg>
</g>
</g>
<rect
fill="transparent"
height="392"
width="780"
x="0"
y="0"
/>
</svg>
</DocumentFragment>
`;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment