refactor: consolidate price delta components (#7268)
* refactor: move delta component out of price chart file * refactor: remove PortfolioArrow * refactor: search row arrow cell * fix: svg prop + snapshots failing unit tests * refactor: css nit
This commit is contained in:
parent
f69226c1d1
commit
bb28235bee
@ -5,13 +5,11 @@ import { useWeb3React } from '@web3-react/core'
|
|||||||
import { sendAnalyticsEvent, TraceEvent } from 'analytics'
|
import { sendAnalyticsEvent, TraceEvent } from 'analytics'
|
||||||
import { ButtonEmphasis, ButtonSize, LoadingButtonSpinner, ThemeButton } from 'components/Button'
|
import { ButtonEmphasis, ButtonSize, LoadingButtonSpinner, ThemeButton } from 'components/Button'
|
||||||
import Column from 'components/Column'
|
import Column from 'components/Column'
|
||||||
import { ArrowChangeDown } from 'components/Icons/ArrowChangeDown'
|
|
||||||
import { ArrowChangeUp } from 'components/Icons/ArrowChangeUp'
|
|
||||||
import { Power } from 'components/Icons/Power'
|
import { Power } from 'components/Icons/Power'
|
||||||
import { Settings } from 'components/Icons/Settings'
|
import { Settings } from 'components/Icons/Settings'
|
||||||
import { AutoRow } from 'components/Row'
|
import { AutoRow } from 'components/Row'
|
||||||
import { LoadingBubble } from 'components/Tokens/loading'
|
import { LoadingBubble } from 'components/Tokens/loading'
|
||||||
import { formatDelta } from 'components/Tokens/TokenDetails/PriceChart'
|
import { DeltaArrow, formatDelta } from 'components/Tokens/TokenDetails/Delta'
|
||||||
import Tooltip from 'components/Tooltip'
|
import Tooltip from 'components/Tooltip'
|
||||||
import { getConnection } from 'connection'
|
import { getConnection } from 'connection'
|
||||||
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
|
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
|
||||||
@ -20,11 +18,11 @@ import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hoo
|
|||||||
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
|
import { useIsNftClaimAvailable } from 'nft/hooks/useIsNftClaimAvailable'
|
||||||
import { ProfilePageStateType } from 'nft/types'
|
import { ProfilePageStateType } from 'nft/types'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { CreditCard, IconProps, Info } from 'react-feather'
|
import { CreditCard, Info } from 'react-feather'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { useAppDispatch } from 'state/hooks'
|
import { useAppDispatch } from 'state/hooks'
|
||||||
import { updateSelectedWallet } from 'state/user/reducer'
|
import { updateSelectedWallet } from 'state/user/reducer'
|
||||||
import styled, { useTheme } from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { CopyHelper, ExternalLink, ThemedText } from 'theme'
|
import { CopyHelper, ExternalLink, ThemedText } from 'theme'
|
||||||
import { shortenAddress } from 'utils'
|
import { shortenAddress } from 'utils'
|
||||||
import { formatNumber, NumberType } from 'utils/formatNumbers'
|
import { formatNumber, NumberType } from 'utils/formatNumbers'
|
||||||
@ -151,15 +149,6 @@ const PortfolioDrawerContainer = styled(Column)`
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
`
|
`
|
||||||
|
|
||||||
export function PortfolioArrow({ change, ...rest }: { change: number } & IconProps) {
|
|
||||||
const theme = useTheme()
|
|
||||||
return change < 0 ? (
|
|
||||||
<ArrowChangeDown color={theme.critical} width={16} {...rest} />
|
|
||||||
) : (
|
|
||||||
<ArrowChangeUp color={theme.success} width={16} {...rest} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AuthenticatedHeader({ account, openSettings }: { account: string; openSettings: () => void }) {
|
export default function AuthenticatedHeader({ account, openSettings }: { account: string; openSettings: () => void }) {
|
||||||
const { connector } = useWeb3React()
|
const { connector } = useWeb3React()
|
||||||
const { ENSName } = useENSName(account)
|
const { ENSName } = useENSName(account)
|
||||||
@ -287,7 +276,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
|
|||||||
<AutoRow marginBottom="20px">
|
<AutoRow marginBottom="20px">
|
||||||
{absoluteChange !== 0 && percentChange && (
|
{absoluteChange !== 0 && percentChange && (
|
||||||
<>
|
<>
|
||||||
<PortfolioArrow change={absoluteChange as number} />
|
<DeltaArrow delta={absoluteChange} />
|
||||||
<ThemedText.BodySecondary>
|
<ThemedText.BodySecondary>
|
||||||
{`${formatNumber({
|
{`${formatNumber({
|
||||||
input: Math.abs(absoluteChange as number),
|
input: Math.abs(absoluteChange as number),
|
||||||
|
@ -2,7 +2,7 @@ import { BrowserEvent, InterfaceElementName, SharedEventName } from '@uniswap/an
|
|||||||
import { TraceEvent } from 'analytics'
|
import { TraceEvent } from 'analytics'
|
||||||
import { useCachedPortfolioBalancesQuery } from 'components/AccountDrawer/PrefetchBalancesWrapper'
|
import { useCachedPortfolioBalancesQuery } from 'components/AccountDrawer/PrefetchBalancesWrapper'
|
||||||
import Row from 'components/Row'
|
import Row from 'components/Row'
|
||||||
import { formatDelta } from 'components/Tokens/TokenDetails/PriceChart'
|
import { DeltaArrow, formatDelta } from 'components/Tokens/TokenDetails/Delta'
|
||||||
import { TokenBalance } from 'graphql/data/__generated__/types-and-hooks'
|
import { TokenBalance } from 'graphql/data/__generated__/types-and-hooks'
|
||||||
import { getTokenDetailsURL, gqlToCurrency, logSentryErrorForUnsupportedChain } from 'graphql/data/util'
|
import { getTokenDetailsURL, gqlToCurrency, logSentryErrorForUnsupportedChain } from 'graphql/data/util'
|
||||||
import { useAtomValue } from 'jotai/utils'
|
import { useAtomValue } from 'jotai/utils'
|
||||||
@ -15,7 +15,6 @@ import { formatNumber, NumberType } from 'utils/formatNumbers'
|
|||||||
import { splitHiddenTokens } from 'utils/splitHiddenTokens'
|
import { splitHiddenTokens } from 'utils/splitHiddenTokens'
|
||||||
|
|
||||||
import { useToggleAccountDrawer } from '../..'
|
import { useToggleAccountDrawer } from '../..'
|
||||||
import { PortfolioArrow } from '../../AuthenticatedHeader'
|
|
||||||
import { hideSmallBalancesAtom } from '../../SmallBalanceToggle'
|
import { hideSmallBalancesAtom } from '../../SmallBalanceToggle'
|
||||||
import { ExpandoRow } from '../ExpandoRow'
|
import { ExpandoRow } from '../ExpandoRow'
|
||||||
import { PortfolioLogo } from '../PortfolioLogo'
|
import { PortfolioLogo } from '../PortfolioLogo'
|
||||||
@ -115,7 +114,7 @@ function TokenRow({ token, quantity, denominatedValue, tokenProjectMarket }: Tok
|
|||||||
})}
|
})}
|
||||||
</ThemedText.SubHeader>
|
</ThemedText.SubHeader>
|
||||||
<Row justify="flex-end">
|
<Row justify="flex-end">
|
||||||
<PortfolioArrow change={percentChange} size={20} strokeWidth={1.75} />
|
<DeltaArrow delta={percentChange} />
|
||||||
<ThemedText.BodySecondary>{formatDelta(percentChange)}</ThemedText.BodySecondary>
|
<ThemedText.BodySecondary>{formatDelta(percentChange)}</ThemedText.BodySecondary>
|
||||||
</Row>
|
</Row>
|
||||||
</>
|
</>
|
||||||
|
@ -20,18 +20,15 @@ import styled from 'styled-components'
|
|||||||
import { ThemedText } from 'theme'
|
import { ThemedText } from 'theme'
|
||||||
import { formatUSDPrice } from 'utils/formatNumbers'
|
import { formatUSDPrice } from 'utils/formatNumbers'
|
||||||
|
|
||||||
import { DeltaText, getDeltaArrow } from '../Tokens/TokenDetails/PriceChart'
|
import { DeltaArrow, DeltaText } from '../Tokens/TokenDetails/Delta'
|
||||||
import { useAddRecentlySearchedAsset } from './RecentlySearchedAssets'
|
import { useAddRecentlySearchedAsset } from './RecentlySearchedAssets'
|
||||||
import * as styles from './SearchBar.css'
|
import * as styles from './SearchBar.css'
|
||||||
|
|
||||||
const PriceChangeContainer = styled.div`
|
const PriceChangeContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
`
|
|
||||||
|
|
||||||
const ArrowCell = styled.span`
|
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
padding-right: 2px;
|
gap: 2px;
|
||||||
`
|
`
|
||||||
|
|
||||||
interface CollectionRowProps {
|
interface CollectionRowProps {
|
||||||
@ -156,8 +153,6 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
|
|||||||
}
|
}
|
||||||
}, [toggleOpen, isHovered, token, navigate, handleClick, tokenDetailsPath])
|
}, [toggleOpen, isHovered, token, navigate, handleClick, tokenDetailsPath])
|
||||||
|
|
||||||
const arrow = getDeltaArrow(token.market?.pricePercentChange?.value, 16)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
data-testid={`searchbar-token-row-${token.chain}-${token.address ?? 'NATIVE'}`}
|
data-testid={`searchbar-token-row-${token.chain}-${token.address ?? 'NATIVE'}`}
|
||||||
@ -192,7 +187,7 @@ export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index,
|
|||||||
<Box className={styles.primaryText}>{formatUSDPrice(token.market.price.value)}</Box>
|
<Box className={styles.primaryText}>{formatUSDPrice(token.market.price.value)}</Box>
|
||||||
</Row>
|
</Row>
|
||||||
<PriceChangeContainer>
|
<PriceChangeContainer>
|
||||||
<ArrowCell>{arrow}</ArrowCell>
|
<DeltaArrow delta={token.market?.pricePercentChange?.value} />
|
||||||
<ThemedText.BodySmall>
|
<ThemedText.BodySmall>
|
||||||
<DeltaText delta={token.market?.pricePercentChange?.value}>
|
<DeltaText delta={token.market?.pricePercentChange?.value}>
|
||||||
{Math.abs(token.market?.pricePercentChange?.value ?? 0).toFixed(2)}%
|
{Math.abs(token.market?.pricePercentChange?.value ?? 0).toFixed(2)}%
|
||||||
|
47
src/components/Tokens/TokenDetails/Delta.tsx
Normal file
47
src/components/Tokens/TokenDetails/Delta.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { ArrowChangeDown } from 'components/Icons/ArrowChangeDown'
|
||||||
|
import { ArrowChangeUp } from 'components/Icons/ArrowChangeUp'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
const StyledUpArrow = styled(ArrowChangeUp)<{ $noColor?: boolean }>`
|
||||||
|
color: ${({ theme, $noColor }) => ($noColor ? theme.neutral2 : theme.success)};
|
||||||
|
`
|
||||||
|
const StyledDownArrow = styled(ArrowChangeDown)<{ $noColor?: boolean }>`
|
||||||
|
color: ${({ theme, $noColor }) => ($noColor ? theme.neutral2 : theme.critical)};
|
||||||
|
`
|
||||||
|
|
||||||
|
export function calculateDelta(start: number, current: number) {
|
||||||
|
return (current / start - 1) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidDelta(delta: number | null | undefined): delta is number {
|
||||||
|
// Null-check not including zero
|
||||||
|
return delta !== null && delta !== undefined && delta !== Infinity && !isNaN(delta)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeltaArrowProps {
|
||||||
|
delta?: number | null
|
||||||
|
noColor?: boolean
|
||||||
|
size?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDelta(delta: number | null | undefined) {
|
||||||
|
if (!isValidDelta(delta)) return '-'
|
||||||
|
|
||||||
|
const formattedDelta = Math.abs(delta).toFixed(2) + '%'
|
||||||
|
return formattedDelta
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeltaArrow({ delta, noColor = false, size = 16 }: DeltaArrowProps) {
|
||||||
|
if (!isValidDelta(delta)) return null
|
||||||
|
|
||||||
|
return Math.sign(delta) < 0 ? (
|
||||||
|
<StyledDownArrow width={size} height={size} key="arrow-down" aria-label="down" $noColor={noColor} />
|
||||||
|
) : (
|
||||||
|
<StyledUpArrow width={size} height={size} key="arrow-up" aria-label="up" $noColor={noColor} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DeltaText = styled.span<{ delta?: number }>`
|
||||||
|
color: ${({ theme, delta }) =>
|
||||||
|
delta !== undefined ? (Math.sign(delta) < 0 ? theme.critical : theme.success) : theme.neutral1};
|
||||||
|
`
|
@ -6,14 +6,12 @@ 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 { ArrowChangeDown } from 'components/Icons/ArrowChangeDown'
|
|
||||||
import { ArrowChangeUp } from 'components/Icons/ArrowChangeUp'
|
|
||||||
import { MouseoverTooltip } from 'components/Tooltip'
|
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, TimePeriod } from 'graphql/data/util'
|
import { PricePoint, 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, Info, TrendingUp } from 'react-feather'
|
import { 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'
|
||||||
@ -27,6 +25,8 @@ import {
|
|||||||
} from 'utils/formatChartTimes'
|
} from 'utils/formatChartTimes'
|
||||||
import { formatUSDPrice } from 'utils/formatNumbers'
|
import { formatUSDPrice } from 'utils/formatNumbers'
|
||||||
|
|
||||||
|
import { calculateDelta, DeltaArrow, formatDelta } from './Delta'
|
||||||
|
|
||||||
const DATA_EMPTY = { value: 0, timestamp: 0 }
|
const DATA_EMPTY = { value: 0, timestamp: 0 }
|
||||||
|
|
||||||
export function getPriceBounds(pricePoints: PricePoint[]): [number, number] {
|
export function getPriceBounds(pricePoints: PricePoint[]): [number, number] {
|
||||||
@ -36,56 +36,6 @@ export function getPriceBounds(pricePoints: PricePoint[]): [number, number] {
|
|||||||
return [min, max]
|
return [min, max]
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledUpArrow = styled(ArrowChangeUp)`
|
|
||||||
color: ${({ theme }) => theme.success};
|
|
||||||
`
|
|
||||||
const StyledDownArrow = styled(ArrowChangeDown)`
|
|
||||||
color: ${({ theme }) => theme.critical};
|
|
||||||
`
|
|
||||||
|
|
||||||
const DefaultUpArrow = styled(ArrowUpRight)`
|
|
||||||
color: ${({ theme }) => theme.neutral3};
|
|
||||||
`
|
|
||||||
const DefaultDownArrow = styled(ArrowDownRight)`
|
|
||||||
color: ${({ theme }) => theme.neutral3};
|
|
||||||
`
|
|
||||||
|
|
||||||
function calculateDelta(start: number, current: number) {
|
|
||||||
return (current / start - 1) * 100
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDeltaArrow(delta: number | null | undefined, iconSize = 16, styled = true) {
|
|
||||||
// Null-check not including zero
|
|
||||||
if (delta === null || delta === undefined) {
|
|
||||||
return null
|
|
||||||
} else if (Math.sign(delta) < 0) {
|
|
||||||
return styled ? (
|
|
||||||
<StyledDownArrow width={iconSize} height={iconSize} key="arrow-down" aria-label="down" />
|
|
||||||
) : (
|
|
||||||
<DefaultDownArrow size={iconSize} key="arrow-down" aria-label="down" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return styled ? (
|
|
||||||
<StyledUpArrow width={iconSize} height={iconSize} key="arrow-up" aria-label="up" />
|
|
||||||
) : (
|
|
||||||
<DefaultUpArrow size={iconSize} key="arrow-up" aria-label="up" />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatDelta(delta: number | null | undefined) {
|
|
||||||
// Null-check not including zero
|
|
||||||
if (delta === null || delta === undefined || delta === Infinity || isNaN(delta)) {
|
|
||||||
return '-'
|
|
||||||
}
|
|
||||||
const formattedDelta = Math.abs(delta).toFixed(2) + '%'
|
|
||||||
return formattedDelta
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DeltaText = styled.span<{ delta?: number }>`
|
|
||||||
color: ${({ theme, delta }) =>
|
|
||||||
delta !== undefined ? (Math.sign(delta) < 0 ? theme.critical : theme.success) : theme.neutral1};
|
|
||||||
`
|
|
||||||
|
|
||||||
const ChartHeader = styled.div`
|
const ChartHeader = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
${textFadeIn};
|
${textFadeIn};
|
||||||
@ -113,10 +63,6 @@ const DeltaContainer = styled.div`
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
color: ${({ theme }) => theme.neutral2};
|
color: ${({ theme }) => theme.neutral2};
|
||||||
`
|
`
|
||||||
export const ArrowCell = styled.div`
|
|
||||||
padding-right: 3px;
|
|
||||||
display: flex;
|
|
||||||
`
|
|
||||||
|
|
||||||
const OutdatedPriceContainer = styled.div`
|
const OutdatedPriceContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -151,6 +97,22 @@ function fixChart(prices: PricePoint[] | undefined | null) {
|
|||||||
const margin = { top: 100, bottom: 48, crosshair: 72 }
|
const margin = { top: 100, bottom: 48, crosshair: 72 }
|
||||||
const timeOptionsHeight = 44
|
const timeOptionsHeight = 44
|
||||||
|
|
||||||
|
interface ChartDeltaProps {
|
||||||
|
startingPrice: PricePoint
|
||||||
|
endingPrice: PricePoint
|
||||||
|
noColor?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChartDelta({ startingPrice, endingPrice, noColor }: ChartDeltaProps) {
|
||||||
|
const delta = calculateDelta(startingPrice.value, endingPrice.value)
|
||||||
|
return (
|
||||||
|
<DeltaContainer>
|
||||||
|
{formatDelta(delta)}
|
||||||
|
<DeltaArrow delta={delta} noColor={noColor} />
|
||||||
|
</DeltaContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
interface PriceChartProps {
|
interface PriceChartProps {
|
||||||
width: number
|
width: number
|
||||||
height: number
|
height: number
|
||||||
@ -202,10 +164,6 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
|
|||||||
return DATA_EMPTY
|
return DATA_EMPTY
|
||||||
}, [prices])
|
}, [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
|
||||||
@ -334,9 +292,6 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updatedTicks = maxTicks > 0 ? (ticks.length > maxTicks ? calculateTicks(ticks) : ticks) : []
|
const updatedTicks = maxTicks > 0 ? (ticks.length > maxTicks ? calculateTicks(ticks) : ticks) : []
|
||||||
const delta = calculateDelta(startingPrice.value, displayPrice.value)
|
|
||||||
const formattedDelta = formatDelta(delta)
|
|
||||||
const arrow = getDeltaArrow(delta)
|
|
||||||
const crosshairEdgeMax = width * 0.85
|
const crosshairEdgeMax = width * 0.85
|
||||||
const crosshairAtEdge = !!crosshair && crosshair > crosshairEdgeMax
|
const crosshairAtEdge = !!crosshair && crosshair > crosshairEdgeMax
|
||||||
|
|
||||||
@ -355,10 +310,7 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
|
|||||||
{displayPrice.value ? (
|
{displayPrice.value ? (
|
||||||
<>
|
<>
|
||||||
<TokenPrice>{formatUSDPrice(displayPrice.value)}</TokenPrice>
|
<TokenPrice>{formatUSDPrice(displayPrice.value)}</TokenPrice>
|
||||||
<DeltaContainer>
|
<ChartDelta startingPrice={startingPrice} endingPrice={displayPrice} />
|
||||||
{formattedDelta}
|
|
||||||
<ArrowCell>{arrow}</ArrowCell>
|
|
||||||
</DeltaContainer>
|
|
||||||
</>
|
</>
|
||||||
) : lastPrice.value ? (
|
) : lastPrice.value ? (
|
||||||
<OutdatedContainer>
|
<OutdatedContainer>
|
||||||
@ -368,10 +320,7 @@ export function PriceChart({ width, height, prices: originalPrices, timePeriod }
|
|||||||
<Info size={16} />
|
<Info size={16} />
|
||||||
</MouseoverTooltip>
|
</MouseoverTooltip>
|
||||||
</OutdatedPriceContainer>
|
</OutdatedPriceContainer>
|
||||||
<DeltaContainer>
|
<ChartDelta startingPrice={firstPrice} endingPrice={lastPrice} noColor />
|
||||||
{formattedTotalDelta}
|
|
||||||
<ArrowCell>{defaultArrow}</ArrowCell>
|
|
||||||
</DeltaContainer>
|
|
||||||
</OutdatedContainer>
|
</OutdatedContainer>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
exports[`PriceChart renders correctly with all prices filled 1`] = `
|
exports[`PriceChart renders correctly with all prices filled 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
.c4 {
|
.c3 {
|
||||||
color: #40B66B;
|
color: #40B66B;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,14 +34,6 @@ exports[`PriceChart renders correctly with all prices filled 1`] = `
|
|||||||
color: #7D7D7D;
|
color: #7D7D7D;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c3 {
|
|
||||||
padding-right: 3px;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="c0"
|
class="c0"
|
||||||
data-cy="chart-header"
|
data-cy="chart-header"
|
||||||
@ -55,12 +47,9 @@ exports[`PriceChart renders correctly with all prices filled 1`] = `
|
|||||||
class="c2"
|
class="c2"
|
||||||
>
|
>
|
||||||
0.00%
|
0.00%
|
||||||
<div
|
|
||||||
class="c3"
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
aria-label="up"
|
aria-label="up"
|
||||||
class="c4"
|
class="c3"
|
||||||
fill="none"
|
fill="none"
|
||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -74,7 +63,6 @@ exports[`PriceChart renders correctly with all prices filled 1`] = `
|
|||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<svg
|
<svg
|
||||||
data-cy="price-chart"
|
data-cy="price-chart"
|
||||||
height="392"
|
height="392"
|
||||||
@ -426,8 +414,8 @@ exports[`PriceChart renders correctly with some prices filled 1`] = `
|
|||||||
height: inherit;
|
height: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c7 {
|
.c6 {
|
||||||
color: #CECECE;
|
color: #7D7D7D;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c0 {
|
.c0 {
|
||||||
@ -462,14 +450,6 @@ exports[`PriceChart renders correctly with some prices filled 1`] = `
|
|||||||
color: #7D7D7D;
|
color: #7D7D7D;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c6 {
|
|
||||||
padding-right: 3px;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c2 {
|
.c2 {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
display: -webkit-flex;
|
display: -webkit-flex;
|
||||||
@ -535,36 +515,23 @@ exports[`PriceChart renders correctly with some prices filled 1`] = `
|
|||||||
class="c5"
|
class="c5"
|
||||||
>
|
>
|
||||||
0.00%
|
0.00%
|
||||||
<div
|
|
||||||
class="c6"
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
aria-label="up"
|
aria-label="up"
|
||||||
class="c7"
|
class="c6"
|
||||||
fill="none"
|
fill="none"
|
||||||
height="20"
|
height="16"
|
||||||
stroke="currentColor"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
width="20"
|
width="16"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<line
|
<path
|
||||||
x1="7"
|
d="M13.3021 7.7547L17.6821 14.2475C18.4182 15.3388 17.7942 17 16.6482 17L7.3518 17C6.2058 17 5.5818 15.3376 6.3179 14.2475L10.6979 7.7547C11.377 6.7484 12.623 6.7484 13.3021 7.7547Z"
|
||||||
x2="17"
|
fill="currentColor"
|
||||||
y1="17"
|
|
||||||
y2="7"
|
|
||||||
/>
|
|
||||||
<polyline
|
|
||||||
points="7 7 17 7 17 17"
|
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<svg
|
<svg
|
||||||
data-cy="price-chart"
|
data-cy="price-chart"
|
||||||
height="392"
|
height="392"
|
||||||
|
@ -33,7 +33,7 @@ import {
|
|||||||
TokenSortMethod,
|
TokenSortMethod,
|
||||||
useSetSortMethod,
|
useSetSortMethod,
|
||||||
} from '../state'
|
} from '../state'
|
||||||
import { ArrowCell, DeltaText, formatDelta, getDeltaArrow } from '../TokenDetails/PriceChart'
|
import { DeltaArrow, DeltaText, formatDelta } from '../TokenDetails/Delta'
|
||||||
|
|
||||||
const Cell = styled.div`
|
const Cell = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -103,8 +103,9 @@ const StyledTokenRow = styled.div<{
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const ClickableContent = styled.div`
|
const ClickableContent = styled.div<{ gap?: number }>`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
${({ gap }) => gap && `gap: ${gap}px`};
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: ${({ theme }) => theme.neutral1};
|
color: ${({ theme }) => theme.neutral1};
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -184,6 +185,7 @@ const PercentChangeInfoCell = styled(Cell)`
|
|||||||
|
|
||||||
@media only screen and (max-width: ${SMALL_MEDIA_BREAKPOINT}) {
|
@media only screen and (max-width: ${SMALL_MEDIA_BREAKPOINT}) {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 3px;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
color: ${({ theme }) => theme.neutral2};
|
color: ${({ theme }) => theme.neutral2};
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -445,8 +447,6 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
|
|||||||
const chainId = supportedChainIdFromGQLChain(filterNetwork)
|
const chainId = supportedChainIdFromGQLChain(filterNetwork)
|
||||||
const timePeriod = useAtomValue(filterTimeAtom)
|
const timePeriod = useAtomValue(filterTimeAtom)
|
||||||
const delta = token.market?.pricePercentChange?.value
|
const delta = token.market?.pricePercentChange?.value
|
||||||
const arrow = getDeltaArrow(delta)
|
|
||||||
const smallArrow = getDeltaArrow(delta, 14)
|
|
||||||
const formattedDelta = formatDelta(delta)
|
const formattedDelta = formatDelta(delta)
|
||||||
|
|
||||||
const exploreTokenSelectedEventProperties = {
|
const exploreTokenSelectedEventProperties = {
|
||||||
@ -489,15 +489,15 @@ export const LoadedRow = forwardRef((props: LoadedRowProps, ref: ForwardedRef<HT
|
|||||||
<PriceInfoCell>
|
<PriceInfoCell>
|
||||||
{price}
|
{price}
|
||||||
<PercentChangeInfoCell>
|
<PercentChangeInfoCell>
|
||||||
<ArrowCell>{smallArrow}</ArrowCell>
|
<DeltaArrow delta={delta} size={14} />
|
||||||
<DeltaText delta={delta}>{formattedDelta}</DeltaText>
|
<DeltaText delta={delta}>{formattedDelta}</DeltaText>
|
||||||
</PercentChangeInfoCell>
|
</PercentChangeInfoCell>
|
||||||
</PriceInfoCell>
|
</PriceInfoCell>
|
||||||
</ClickableContent>
|
</ClickableContent>
|
||||||
}
|
}
|
||||||
percentChange={
|
percentChange={
|
||||||
<ClickableContent>
|
<ClickableContent gap={3}>
|
||||||
<ArrowCell>{arrow}</ArrowCell>
|
<DeltaArrow delta={delta} />
|
||||||
<DeltaText delta={delta}>{formattedDelta}</DeltaText>
|
<DeltaText delta={delta}>{formattedDelta}</DeltaText>
|
||||||
</ClickableContent>
|
</ClickableContent>
|
||||||
}
|
}
|
||||||
|
@ -2,22 +2,14 @@
|
|||||||
|
|
||||||
exports[`LoadedRow.tsx renders a row 1`] = `
|
exports[`LoadedRow.tsx renders a row 1`] = `
|
||||||
<DocumentFragment>
|
<DocumentFragment>
|
||||||
.c19 {
|
.c18 {
|
||||||
color: #40B66B;
|
color: #40B66B;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c20 {
|
.c19 {
|
||||||
color: #40B66B;
|
color: #40B66B;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c18 {
|
|
||||||
padding-right: 3px;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -webkit-flex;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c9 {
|
.c9 {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
-webkit-transition: opacity 250ms ease-in;
|
-webkit-transition: opacity 250ms ease-in;
|
||||||
@ -113,6 +105,22 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c21 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
gap: 3px;
|
||||||
|
-webkit-text-decoration: none;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #222222;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.c6 {
|
.c6 {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@ -156,7 +164,7 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
|||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c21 {
|
.c20 {
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +272,7 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (max-width:540px) {
|
@media only screen and (max-width:540px) {
|
||||||
.c21 {
|
.c20 {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -275,6 +283,7 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
|||||||
display: -webkit-flex;
|
display: -webkit-flex;
|
||||||
display: -ms-flexbox;
|
display: -ms-flexbox;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 3px;
|
||||||
-webkit-box-pack: end;
|
-webkit-box-pack: end;
|
||||||
-webkit-justify-content: flex-end;
|
-webkit-justify-content: flex-end;
|
||||||
-ms-flex-pack: end;
|
-ms-flex-pack: end;
|
||||||
@ -407,13 +416,10 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
|||||||
$1.00
|
$1.00
|
||||||
<div
|
<div
|
||||||
class="c2 c17"
|
class="c2 c17"
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="c18"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-label="up"
|
aria-label="up"
|
||||||
class="c19"
|
class="c18"
|
||||||
fill="none"
|
fill="none"
|
||||||
height="14"
|
height="14"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -425,9 +431,8 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
|
||||||
<span
|
<span
|
||||||
class="c20"
|
class="c19"
|
||||||
>
|
>
|
||||||
0.00%
|
0.00%
|
||||||
</span>
|
</span>
|
||||||
@ -436,18 +441,15 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="c2 c14 c21"
|
class="c2 c14 c20"
|
||||||
data-testid="percent-change-cell"
|
data-testid="percent-change-cell"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="c5"
|
class="c21"
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="c18"
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
aria-label="up"
|
aria-label="up"
|
||||||
class="c19"
|
class="c18"
|
||||||
fill="none"
|
fill="none"
|
||||||
height="16"
|
height="16"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -459,9 +461,8 @@ exports[`LoadedRow.tsx renders a row 1`] = `
|
|||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
|
||||||
<span
|
<span
|
||||||
class="c20"
|
class="c19"
|
||||||
>
|
>
|
||||||
0.00%
|
0.00%
|
||||||
</span>
|
</span>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getDeltaArrow } from 'components/Tokens/TokenDetails/PriceChart'
|
import { DeltaArrow } from 'components/Tokens/TokenDetails/Delta'
|
||||||
import { useScreenSize } from 'hooks/useScreenSize'
|
import { useScreenSize } from 'hooks/useScreenSize'
|
||||||
import { Box, BoxProps } from 'nft/components/Box'
|
import { Box, BoxProps } from 'nft/components/Box'
|
||||||
import { Column, Row } from 'nft/components/Flex'
|
import { Column, Row } from 'nft/components/Flex'
|
||||||
@ -352,7 +352,6 @@ const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMob
|
|||||||
const floorPriceStr = floorFormatter(stats.stats?.floor_price ?? 0)
|
const floorPriceStr = floorFormatter(stats.stats?.floor_price ?? 0)
|
||||||
// graphQL formatted %age values out of 100, whereas v3 endpoint did a decimal between 0 & 1
|
// graphQL formatted %age values out of 100, whereas v3 endpoint did a decimal between 0 & 1
|
||||||
const floorChangeStr = Math.round(Math.abs(stats?.stats?.one_day_floor_change ?? 0))
|
const floorChangeStr = Math.round(Math.abs(stats?.stats?.one_day_floor_change ?? 0))
|
||||||
const arrow = stats?.stats?.one_day_floor_change ? getDeltaArrow(stats.stats.one_day_floor_change) : undefined
|
|
||||||
|
|
||||||
const isBagExpanded = useBag((state) => state.bagExpanded)
|
const isBagExpanded = useBag((state) => state.bagExpanded)
|
||||||
const isScreenSize = useScreenSize()
|
const isScreenSize = useScreenSize()
|
||||||
@ -372,7 +371,7 @@ const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMob
|
|||||||
{stats.stats?.one_day_floor_change !== undefined ? (
|
{stats.stats?.one_day_floor_change !== undefined ? (
|
||||||
<StatsItem label="Floor 24H" shouldHide={false}>
|
<StatsItem label="Floor 24H" shouldHide={false}>
|
||||||
<PercentChange isNegative={stats.stats.one_day_floor_change < 0}>
|
<PercentChange isNegative={stats.stats.one_day_floor_change < 0}>
|
||||||
{arrow}
|
<DeltaArrow delta={stats?.stats?.one_day_floor_change} />
|
||||||
{floorChangeStr}%
|
{floorChangeStr}%
|
||||||
</PercentChange>
|
</PercentChange>
|
||||||
</StatsItem>
|
</StatsItem>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { formatEther } from '@ethersproject/units'
|
import { formatEther } from '@ethersproject/units'
|
||||||
import { ArrowChangeDown } from 'components/Icons/ArrowChangeDown'
|
import { DeltaArrow } from 'components/Tokens/TokenDetails/Delta'
|
||||||
import { ArrowChangeUp } from 'components/Icons/ArrowChangeUp'
|
|
||||||
import { VerifiedIcon } from 'nft/components/icons'
|
import { VerifiedIcon } from 'nft/components/icons'
|
||||||
import { useIsMobile } from 'nft/hooks'
|
import { useIsMobile } from 'nft/hooks'
|
||||||
import { Denomination } from 'nft/types'
|
import { Denomination } from 'nft/types'
|
||||||
@ -162,11 +161,7 @@ export const ChangeCell = ({ change, children }: { children?: ReactNode; change?
|
|||||||
const TextComponent = isMobile ? ThemedText.BodySmall : ThemedText.BodyPrimary
|
const TextComponent = isMobile ? ThemedText.BodySmall : ThemedText.BodyPrimary
|
||||||
return (
|
return (
|
||||||
<ChangeCellContainer change={change ?? 0}>
|
<ChangeCellContainer change={change ?? 0}>
|
||||||
{!change || change > 0 ? (
|
<DeltaArrow delta={change} />
|
||||||
<ArrowChangeUp width="16px" height="16px" />
|
|
||||||
) : (
|
|
||||||
<ArrowChangeDown width="16px" height="16px" />
|
|
||||||
)}
|
|
||||||
<TextComponent color="currentColor">{children || `${change ? Math.abs(Math.round(change)) : 0}%`}</TextComponent>
|
<TextComponent color="currentColor">{children || `${change ? Math.abs(Math.round(change)) : 0}%`}</TextComponent>
|
||||||
</ChangeCellContainer>
|
</ChangeCellContainer>
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user