Commit 4442933e authored by cartcrom's avatar cartcrom Committed by GitHub

feat: fetch top tokens based on volume & update data descriptions (#5360)

* replaced info icons w/ popover text
* replaced tooltip components w/ existing mouseover component
* fix: updating useCallback dependencies in tooltip component
* fix: removing redundant space
parent 8447b303
import Tooltip from 'components/Tooltip'
import { ReactNode, useCallback, useState } from 'react'
import { Info } from 'react-feather'
import styled from 'styled-components/macro'
const InfoTipContainer = styled.div`
display: flex;
position: relative;
align-items: center;
cursor: help;
`
const InfoTipBody = styled.div`
color: ${({ theme }) => theme.textPrimary};
font-weight: 400;
font-size: 12px;
line-height: 16px;
`
const InfoTipWrapper = styled.div`
margin-left: 4px;
display: flex;
align-items: center;
`
export default function InfoTip({ text }: { text: ReactNode; size?: number }) {
const [show, setShow] = useState<boolean>(false)
const open = useCallback(() => setShow(true), [setShow])
const close = useCallback(() => setShow(false), [setShow])
return (
<InfoTipWrapper>
<Tooltip text={<InfoTipBody>{text}</InfoTipBody>} show={show} placement="right">
<InfoTipContainer onClick={open} onMouseEnter={open} onMouseLeave={close}>
<Info size={14} />
</InfoTipContainer>
</Tooltip>
</InfoTipWrapper>
)
}
import { Trans } from '@lingui/macro' import { Trans } from '@lingui/macro'
import { formatNumber, NumberType } from '@uniswap/conedison/format' import { formatNumber, NumberType } from '@uniswap/conedison/format'
import { MouseoverTooltip } from 'components/Tooltip'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import styled from 'styled-components/macro' import styled from 'styled-components/macro'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
...@@ -7,16 +8,12 @@ import { textFadeIn } from 'theme/styles' ...@@ -7,16 +8,12 @@ import { textFadeIn } from 'theme/styles'
import { TokenSortMethod } from '../state' import { TokenSortMethod } from '../state'
import { HEADER_DESCRIPTIONS } from '../TokenTable/TokenRow' import { HEADER_DESCRIPTIONS } from '../TokenTable/TokenRow'
import InfoTip from './InfoTip'
export const StatWrapper = styled.div` export const StatWrapper = styled.div`
display: flex;
flex-direction: column;
color: ${({ theme }) => theme.textSecondary}; color: ${({ theme }) => theme.textSecondary};
font-size: 14px; font-size: 14px;
min-width: 168px; min-width: 168px;
flex: 1; flex: 1;
gap: 4px;
padding: 24px 0px; padding: 24px 0px;
` `
const TokenStatsSection = styled.div` const TokenStatsSection = styled.div`
...@@ -32,12 +29,9 @@ export const StatPair = styled.div` ...@@ -32,12 +29,9 @@ export const StatPair = styled.div`
const Header = styled(ThemedText.MediumHeader)` const Header = styled(ThemedText.MediumHeader)`
font-size: 28px !important; font-size: 28px !important;
` `
const StatTitle = styled.div`
display: flex; const StatPrice = styled.div`
flex-direction: row; margin-top: 4px;
gap: 4px;
`
const StatPrice = styled.span`
font-size: 28px; font-size: 28px;
color: ${({ theme }) => theme.textPrimary}; color: ${({ theme }) => theme.textPrimary};
` `
...@@ -54,10 +48,7 @@ type NumericStat = number | undefined | null ...@@ -54,10 +48,7 @@ type NumericStat = number | undefined | null
function Stat({ value, title, description }: { value: NumericStat; title: ReactNode; description?: ReactNode }) { function Stat({ value, title, description }: { value: NumericStat; title: ReactNode; description?: ReactNode }) {
return ( return (
<StatWrapper> <StatWrapper>
<StatTitle> <MouseoverTooltip text={description}>{title}</MouseoverTooltip>
{title}
{description && <InfoTip text={description}></InfoTip>}
</StatTitle>
<StatPrice>{formatNumber(value, NumberType.FiatTokenStats)}</StatPrice> <StatPrice>{formatNumber(value, NumberType.FiatTokenStats)}</StatPrice>
</StatWrapper> </StatWrapper>
) )
......
...@@ -5,13 +5,14 @@ import { formatNumber, formatUSDPrice, NumberType } from '@uniswap/conedison/for ...@@ -5,13 +5,14 @@ import { formatNumber, formatUSDPrice, NumberType } from '@uniswap/conedison/for
import { ParentSize } from '@visx/responsive' import { ParentSize } from '@visx/responsive'
import SparklineChart from 'components/Charts/SparklineChart' import SparklineChart from 'components/Charts/SparklineChart'
import QueryTokenLogo from 'components/Logo/QueryTokenLogo' import QueryTokenLogo from 'components/Logo/QueryTokenLogo'
import { MouseoverTooltip } from 'components/Tooltip'
import { getChainInfo } from 'constants/chainInfo' import { getChainInfo } from 'constants/chainInfo'
import { SparklineMap, TopToken } from 'graphql/data/TopTokens' import { SparklineMap, TopToken } from 'graphql/data/TopTokens'
import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util' import { CHAIN_NAME_TO_CHAIN_ID, getTokenDetailsURL } from 'graphql/data/util'
import { useAtomValue } from 'jotai/utils' import { useAtomValue } from 'jotai/utils'
import { ForwardedRef, forwardRef } from 'react' import { ForwardedRef, forwardRef } from 'react'
import { CSSProperties, ReactNode } from 'react' import { CSSProperties, ReactNode } from 'react'
import { ArrowDown, ArrowUp } from 'react-feather' import { ArrowDown, ArrowUp, Info } from 'react-feather'
import { Link, useParams } from 'react-router-dom' import { Link, useParams } from 'react-router-dom'
import styled, { css, useTheme } from 'styled-components/macro' import styled, { css, useTheme } from 'styled-components/macro'
import { ClickableStyle } from 'theme' import { ClickableStyle } from 'theme'
...@@ -31,7 +32,6 @@ import { ...@@ -31,7 +32,6 @@ import {
TokenSortMethod, TokenSortMethod,
useSetSortMethod, useSetSortMethod,
} from '../state' } from '../state'
import InfoTip from '../TokenDetails/InfoTip'
import { ArrowCell, DeltaText, formatDelta, getDeltaArrow } from '../TokenDetails/PriceChart' import { ArrowCell, DeltaText, formatDelta, getDeltaArrow } from '../TokenDetails/PriceChart'
const Cell = styled.div` const Cell = styled.div`
...@@ -200,7 +200,6 @@ const HeaderCellWrapper = styled.span<{ onClick?: () => void }>` ...@@ -200,7 +200,6 @@ const HeaderCellWrapper = styled.span<{ onClick?: () => void }>`
cursor: ${({ onClick }) => (onClick ? 'pointer' : 'unset')}; cursor: ${({ onClick }) => (onClick ? 'pointer' : 'unset')};
display: flex; display: flex;
gap: 4px; gap: 4px;
height: 100%;
justify-content: flex-end; justify-content: flex-end;
width: 100%; width: 100%;
...@@ -297,6 +296,13 @@ export const LogoContainer = styled.div` ...@@ -297,6 +296,13 @@ export const LogoContainer = styled.div`
display: flex; display: flex;
` `
const InfoIconContainer = styled.div`
margin-left: 2px;
display: flex;
align-items: center;
cursor: help;
`
export const HEADER_DESCRIPTIONS: Record<TokenSortMethod, ReactNode | undefined> = { export const HEADER_DESCRIPTIONS: Record<TokenSortMethod, ReactNode | undefined> = {
[TokenSortMethod.PRICE]: undefined, [TokenSortMethod.PRICE]: undefined,
[TokenSortMethod.PERCENT_CHANGE]: undefined, [TokenSortMethod.PERCENT_CHANGE]: undefined,
...@@ -333,7 +339,13 @@ function HeaderCell({ ...@@ -333,7 +339,13 @@ function HeaderCell({
</> </>
)} )}
{category} {category}
{description && <InfoTip text={description}></InfoTip>} {description && (
<MouseoverTooltip text={description} placement="right">
<InfoIconContainer>
<Info size={14} />
</InfoIconContainer>
</MouseoverTooltip>
)}
</HeaderCellWrapper> </HeaderCellWrapper>
) )
} }
......
...@@ -8,7 +8,11 @@ export const TooltipContainer = styled.div` ...@@ -8,7 +8,11 @@ export const TooltipContainer = styled.div`
max-width: 256px; max-width: 256px;
cursor: default; cursor: default;
padding: 0.6rem 1rem; padding: 0.6rem 1rem;
color: ${({ theme }) => theme.textPrimary};
font-weight: 400; font-weight: 400;
font-size: 12px;
line-height: 16px;
word-break: break-word; word-break: break-word;
background: ${({ theme }) => theme.backgroundSurface}; background: ${({ theme }) => theme.backgroundSurface};
...@@ -56,7 +60,7 @@ function TooltipContent({ content, wrap = false, ...rest }: TooltipContentProps) ...@@ -56,7 +60,7 @@ function TooltipContent({ content, wrap = false, ...rest }: TooltipContentProps)
/** Standard text tooltip. */ /** Standard text tooltip. */
export function MouseoverTooltip({ text, disableHover, children, timeout, ...rest }: Omit<TooltipProps, 'show'>) { export function MouseoverTooltip({ text, disableHover, children, timeout, ...rest }: Omit<TooltipProps, 'show'>) {
const [show, setShow] = useState(false) const [show, setShow] = useState(false)
const open = useCallback(() => setShow(true), [setShow]) const open = useCallback(() => text && setShow(true), [text, setShow])
const close = useCallback(() => setShow(false), [setShow]) const close = useCallback(() => setShow(false), [setShow])
useEffect(() => { useEffect(() => {
......
...@@ -19,7 +19,7 @@ import { CHAIN_NAME_TO_CHAIN_ID, isPricePoint, PricePoint, toHistoryDuration, un ...@@ -19,7 +19,7 @@ import { CHAIN_NAME_TO_CHAIN_ID, isPricePoint, PricePoint, toHistoryDuration, un
gql` gql`
query TopTokens100($duration: HistoryDuration!, $chain: Chain!) { query TopTokens100($duration: HistoryDuration!, $chain: Chain!) {
topTokens(pageSize: 100, page: 1, chain: $chain) { topTokens(pageSize: 100, page: 1, chain: $chain, orderBy: VOLUME) {
id id
name name
chain chain
......
...@@ -8,6 +8,7 @@ import NetworkFilter from 'components/Tokens/TokenTable/NetworkFilter' ...@@ -8,6 +8,7 @@ import NetworkFilter from 'components/Tokens/TokenTable/NetworkFilter'
import SearchBar from 'components/Tokens/TokenTable/SearchBar' import SearchBar from 'components/Tokens/TokenTable/SearchBar'
import TimeSelector from 'components/Tokens/TokenTable/TimeSelector' import TimeSelector from 'components/Tokens/TokenTable/TimeSelector'
import TokenTable from 'components/Tokens/TokenTable/TokenTable' import TokenTable from 'components/Tokens/TokenTable/TokenTable'
import { MouseoverTooltip } from 'components/Tooltip'
import { chainIdToBackendName, isValidBackendChainName } from 'graphql/data/util' import { chainIdToBackendName, isValidBackendChainName } from 'graphql/data/util'
import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch' import { useOnGlobalChainSwitch } from 'hooks/useGlobalChainSwitch'
import { useResetAtom } from 'jotai/utils' import { useResetAtom } from 'jotai/utils'
...@@ -46,8 +47,8 @@ const FiltersContainer = styled.div` ...@@ -46,8 +47,8 @@ const FiltersContainer = styled.div`
} }
` `
const SearchContainer = styled(FiltersContainer)` const SearchContainer = styled(FiltersContainer)`
width: 100%;
margin-left: 8px; margin-left: 8px;
width: 100%;
@media only screen and (max-width: ${MEDIUM_MEDIA_BREAKPOINT}) { @media only screen and (max-width: ${MEDIUM_MEDIA_BREAKPOINT}) {
margin: 0px; margin: 0px;
...@@ -59,6 +60,8 @@ const FiltersWrapper = styled.div` ...@@ -59,6 +60,8 @@ const FiltersWrapper = styled.div`
max-width: ${MAX_WIDTH_MEDIA_BREAKPOINT}; max-width: ${MAX_WIDTH_MEDIA_BREAKPOINT};
margin: 0 auto; margin: 0 auto;
margin-bottom: 20px; margin-bottom: 20px;
color: ${({ theme }) => theme.textTertiary};
flex-direction: row;
@media only screen and (max-width: ${MEDIUM_MEDIA_BREAKPOINT}) { @media only screen and (max-width: ${MEDIUM_MEDIA_BREAKPOINT}) {
flex-direction: column; flex-direction: column;
...@@ -94,9 +97,14 @@ const Tokens = () => { ...@@ -94,9 +97,14 @@ const Tokens = () => {
<Trace page={PageName.TOKENS_PAGE} shouldLogImpression> <Trace page={PageName.TOKENS_PAGE} shouldLogImpression>
<ExploreContainer> <ExploreContainer>
<TitleContainer> <TitleContainer>
<MouseoverTooltip
text={<Trans>This table contains the top tokens by Uniswap volume, sorted based on your input.</Trans>}
placement="bottom"
>
<ThemedText.LargeHeader> <ThemedText.LargeHeader>
<Trans>Top tokens on Uniswap</Trans> <Trans>Top tokens on Uniswap</Trans>
</ThemedText.LargeHeader> </ThemedText.LargeHeader>
</MouseoverTooltip>
</TitleContainer> </TitleContainer>
<FiltersWrapper> <FiltersWrapper>
<FiltersContainer> <FiltersContainer>
......
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