Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d23b6e5da6 | ||
|
|
44c355c7f0 | ||
|
|
e4a9764a12 | ||
|
|
303fa15240 | ||
|
|
d180aef306 | ||
|
|
c07c401189 | ||
|
|
65d91eb363 |
@@ -14,6 +14,7 @@ module.exports = {
|
||||
rules: {
|
||||
'multiline-comment-style': ['error', 'separate-lines'],
|
||||
'rulesdir/enforce-retry-on-import': 'error',
|
||||
'rulesdir/no-undefined-or': 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
8
.github/actions/setup/action.yml
vendored
8
.github/actions/setup/action.yml
vendored
@@ -53,6 +53,14 @@ runs:
|
||||
shell: bash
|
||||
|
||||
# Messages are extracted from source.
|
||||
# A record of source file content hashes and catalogs is maintained in node_modules/.cache/lingui.
|
||||
# Messages are always extracted, but extraction may short-circuit from the custom extractor's cache.
|
||||
- uses: actions/cache@v3
|
||||
id: i18n-extract-cache
|
||||
with:
|
||||
path: node_modules/.cache
|
||||
key: ${{ runner.os }}-i18n-extract-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-i18n-extract-
|
||||
- run: yarn i18n:extract
|
||||
shell: bash
|
||||
|
||||
|
||||
20
.github/workflows/test.yml
vendored
20
.github/workflows/test.yml
vendored
@@ -16,8 +16,6 @@ on:
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: ${{ github.ref_name == 'main' && 'notify/test' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
@@ -32,12 +30,10 @@ jobs:
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Lint
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: ${{ github.ref_name == 'main' && 'notify/test' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
@@ -52,12 +48,10 @@ jobs:
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Typecheck
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
deps-tests:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: ${{ github.ref_name == 'main' && 'notify/test' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
@@ -66,12 +60,10 @@ jobs:
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Dependency checks
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: ${{ github.ref_name == 'main' && 'notify/test' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
@@ -92,7 +84,7 @@ jobs:
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Unit tests
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
build-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -123,8 +115,6 @@ jobs:
|
||||
cypress-test-matrix:
|
||||
needs: [build-e2e, cypress-rerun]
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: ${{ github.ref_name == 'main' && 'notify/test' }}
|
||||
container: cypress/browsers:node-18.14.1-chrome-111.0.5563.64-1-ff-111.0-edge-111.0.1661.43-1
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -181,7 +171,7 @@ jobs:
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Cypress tests
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
# Included as a single job to check for cypress-test-matrix success, as a matrix cannot be checked.
|
||||
cypress-tests:
|
||||
|
||||
44
eslint_rules/no-undefined-or.js
Normal file
44
eslint_rules/no-undefined-or.js
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Enforce the use of optional object fields',
|
||||
category: 'Best Practices',
|
||||
recommended: true,
|
||||
},
|
||||
fixable: 'code',
|
||||
schema: [],
|
||||
},
|
||||
create(context) {
|
||||
return {
|
||||
'TSPropertySignature > TSTypeAnnotation > TSUnionType': (node) => {
|
||||
const types = node.types
|
||||
const hasUndefined = types.some((typeNode) => typeNode.type === 'TSUndefinedKeyword')
|
||||
|
||||
if (hasUndefined) {
|
||||
const typesWithoutUndefined = types.filter((typeNode) => typeNode.type !== 'TSUndefinedKeyword')
|
||||
|
||||
// If there is more than one type left after removing 'undefined',
|
||||
// join them together with ' | ' to create a new union type.
|
||||
const newTypeSource =
|
||||
typesWithoutUndefined.length > 1
|
||||
? typesWithoutUndefined.map((typeNode) => context.getSourceCode().getText(typeNode)).join(' | ')
|
||||
: context.getSourceCode().getText(typesWithoutUndefined[0])
|
||||
|
||||
context.report({
|
||||
node,
|
||||
message: `Prefer optional properties to "Type | undefined".`,
|
||||
fix(fixer) {
|
||||
const propertySignature = node.parent.parent
|
||||
const isAlreadyOptional = propertySignature.optional
|
||||
const newTypeAnnotation = isAlreadyOptional ? `: ${newTypeSource}` : `?: ${newTypeSource}`
|
||||
return fixer.replaceText(node.parent, newTypeAnnotation)
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,3 +1,65 @@
|
||||
/* eslint-env node */
|
||||
import { default as babelExtractor } from '@lingui/cli/api/extractors/babel'
|
||||
import { createHash } from 'crypto'
|
||||
import { mkdirSync, readFileSync, writeFileSync } from 'fs'
|
||||
import { existsSync } from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
/** A custom caching extractor built on top of babelExtractor. */
|
||||
const cachingExtractor: typeof babelExtractor = {
|
||||
/** Delegates to babelExtractor.match. */
|
||||
match(filename: string) {
|
||||
return babelExtractor.match(filename)
|
||||
},
|
||||
/**
|
||||
* Checks a cache before extraction, only delegating to babelExtractor.extract if the file has changed.
|
||||
*
|
||||
* The lingui extractor works by extracting JSON (the catalog) from `filename` to `buildDir/filename.json`.
|
||||
* Caching works by man-in-the-middling this:
|
||||
* - File freshness is computed as a hash of `filename` contents.
|
||||
* - Before extracting, we check the cache to see if we already have a fresh catalog for the file.
|
||||
* If we do, we copy it to `localeDir/filename.json`. Copying is significantly faster than extracting.
|
||||
* - After extracting, we copy the catalog to the cache.
|
||||
*/
|
||||
extract(filename: string, localeDir: string, ...options: unknown[]) {
|
||||
// This runs from node_modules/@lingui/conf, so we need to back out to the root.
|
||||
const root = __dirname.split('/node_modules')[0]
|
||||
|
||||
// This logic mimics catalogFilename in @lingui/babel-plugin-extract-messages.
|
||||
const buildDir = path.join(localeDir, '_build')
|
||||
const localePath = path.join(buildDir, filename + '.json')
|
||||
|
||||
const filePath = path.join(root, filename)
|
||||
const fileHash = createHash('sha256').update(readFileSync(filePath)).digest('hex')
|
||||
|
||||
const cacheRoot = path.join(root, 'node_modules/.cache/lingui')
|
||||
const cachePath = path.join(cacheRoot, filename + '.json')
|
||||
|
||||
// If we have a matching cached copy of the catalog, we can copy it to localePath and return early.
|
||||
if (existsSync(cachePath)) {
|
||||
const { hash, catalog } = JSON.parse(readFileSync(cachePath, 'utf8'))
|
||||
if (hash === fileHash) {
|
||||
if (catalog) {
|
||||
mkdirSync(path.dirname(localePath), { recursive: true })
|
||||
writeFileSync(localePath, JSON.stringify(catalog, null, 2))
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
babelExtractor.extract(filename, localeDir, ...options)
|
||||
|
||||
// Cache the extracted catalog.
|
||||
mkdirSync(path.dirname(cachePath), { recursive: true })
|
||||
if (existsSync(localePath)) {
|
||||
const catalog = JSON.parse(readFileSync(localePath, 'utf8'))
|
||||
writeFileSync(cachePath, JSON.stringify({ hash: fileHash, catalog }))
|
||||
} else {
|
||||
writeFileSync(cachePath, JSON.stringify({ hash: fileHash }))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const linguiConfig = {
|
||||
catalogs: [
|
||||
{
|
||||
@@ -60,6 +122,7 @@ const linguiConfig = {
|
||||
runtimeConfigModule: ['@lingui/core', 'i18n'],
|
||||
sourceLocale: 'en-US',
|
||||
pseudoLocale: 'pseudo',
|
||||
extractors: [cachingExtractor],
|
||||
}
|
||||
|
||||
export default linguiConfig
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"@types/rebass": "^4.0.7",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"@types/testing-library__cypress": "^5.0.5",
|
||||
"@types/ua-parser-js": "^0.7.35",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@uniswap/default-token-list": "^9.4.0",
|
||||
@@ -247,7 +247,7 @@
|
||||
"statsig-react": "^1.22.0",
|
||||
"styled-components": "^5.3.5",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"use-resize-observer": "^9.0.2",
|
||||
"uuid": "^8.3.2",
|
||||
"video-extensions": "^1.2.0",
|
||||
|
||||
@@ -49,7 +49,7 @@ const DEFAULT_CHAINS = [
|
||||
SupportedChainId.CELO,
|
||||
]
|
||||
|
||||
type UseMultiChainPositionsData = { positions: PositionInfo[] | undefined; loading: boolean }
|
||||
type UseMultiChainPositionsData = { positions?: PositionInfo[]; loading: boolean }
|
||||
|
||||
/**
|
||||
* Returns all positions for a given account on multiple chains.
|
||||
|
||||
31
src/components/AnimatedDropdown/index.test.tsx
Normal file
31
src/components/AnimatedDropdown/index.test.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { render, screen, waitFor } from 'test-utils/render'
|
||||
|
||||
import AnimatedDropdown from './index'
|
||||
|
||||
describe('AnimatedDropdown', () => {
|
||||
it('does not render children when closed', () => {
|
||||
render(<AnimatedDropdown open={false}>Body</AnimatedDropdown>)
|
||||
expect(screen.getByText('Body')).not.toBeVisible()
|
||||
})
|
||||
|
||||
it('renders children when open', () => {
|
||||
render(<AnimatedDropdown open={true}>Body</AnimatedDropdown>)
|
||||
expect(screen.getByText('Body')).toBeVisible()
|
||||
})
|
||||
|
||||
it('animates when open changes', async () => {
|
||||
const { rerender } = render(<AnimatedDropdown open={false}>Body</AnimatedDropdown>)
|
||||
|
||||
const body = screen.getByText('Body')
|
||||
|
||||
expect(body).not.toBeVisible()
|
||||
|
||||
rerender(<AnimatedDropdown open={true}>Body</AnimatedDropdown>)
|
||||
expect(body).not.toBeVisible()
|
||||
|
||||
// wait for React Spring animation to finish
|
||||
await waitFor(() => {
|
||||
expect(body).toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -9,7 +9,10 @@ export default function AnimatedDropdown({ open, children }: React.PropsWithChil
|
||||
const { ref, height } = useResizeObserver()
|
||||
|
||||
const props = useSpring({
|
||||
height: open ? height ?? 0 : 0,
|
||||
// On initial render, `height` will be undefined as ref has not been set yet.
|
||||
// If the dropdown should be open, we fallback to `auto` to avoid flickering.
|
||||
// Otherwise, we just animate between actual height (when open) and 0 (when closed).
|
||||
height: open ? height ?? 'auto' : 0,
|
||||
config: {
|
||||
mass: 1.2,
|
||||
tension: 300,
|
||||
@@ -20,14 +23,7 @@ export default function AnimatedDropdown({ open, children }: React.PropsWithChil
|
||||
})
|
||||
|
||||
return (
|
||||
<animated.div
|
||||
style={{
|
||||
...props,
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
willChange: 'height',
|
||||
}}
|
||||
>
|
||||
<animated.div style={{ ...props, overflow: 'hidden', width: '100%', willChange: 'height' }}>
|
||||
<div ref={ref}>{children}</div>
|
||||
</animated.div>
|
||||
)
|
||||
|
||||
@@ -32,13 +32,7 @@ const LabelText = styled.div<{ color: string }>`
|
||||
justify-content: flex-end;
|
||||
`
|
||||
|
||||
export default function RangeBadge({
|
||||
removed,
|
||||
inRange,
|
||||
}: {
|
||||
removed: boolean | undefined
|
||||
inRange: boolean | undefined
|
||||
}) {
|
||||
export default function RangeBadge({ removed, inRange }: { removed?: boolean; inRange?: boolean }) {
|
||||
const theme = useTheme()
|
||||
return (
|
||||
<BadgeWrapper>
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useHideUniswapWalletBanner } from 'state/user/hooks'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
import { Z_INDEX } from 'theme/zIndex'
|
||||
import { isIOS } from 'utils/userAgent'
|
||||
import { isIOS, isMobileSafari } from 'utils/userAgent'
|
||||
|
||||
const PopupContainer = styled.div<{ show: boolean }>`
|
||||
display: flex;
|
||||
@@ -93,6 +93,8 @@ export default function UniswapWalletBanner() {
|
||||
|
||||
const screenSize = useScreenSize()
|
||||
|
||||
if (isMobileSafari) return null
|
||||
|
||||
return (
|
||||
<PopupContainer show={shouldDisplay}>
|
||||
<StyledXButton
|
||||
|
||||
@@ -19,7 +19,7 @@ interface SparklineChartProps {
|
||||
width: number
|
||||
height: number
|
||||
tokenData: TopToken
|
||||
pricePercentChange: number | undefined | null
|
||||
pricePercentChange?: number | null
|
||||
sparklineMap: SparklineMap
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const ContentWrapper = styled(Column)`
|
||||
font-size: 12px;
|
||||
`
|
||||
interface ConnectedAccountBlockedProps {
|
||||
account: string | null | undefined
|
||||
account?: string | null
|
||||
isOpen: boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { t } from '@lingui/macro'
|
||||
import { formatNumber, formatPriceImpact, NumberType } from '@uniswap/conedison/format'
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import Row from 'components/Row'
|
||||
import { LoadingBubble } from 'components/Tokens/loading'
|
||||
import { MouseoverTooltip } from 'components/Tooltip'
|
||||
import { useMemo } from 'react'
|
||||
import styled, { useTheme } from 'styled-components/macro'
|
||||
|
||||
import { ThemedText } from '../../theme'
|
||||
import { warningSeverity } from '../../utils/prices'
|
||||
import styled from 'styled-components/macro'
|
||||
import { ThemedText } from 'theme'
|
||||
import { warningSeverity } from 'utils/prices'
|
||||
|
||||
const FiatLoadingBubble = styled(LoadingBubble)`
|
||||
border-radius: 4px;
|
||||
@@ -21,36 +19,40 @@ export function FiatValue({
|
||||
fiatValue,
|
||||
priceImpact,
|
||||
}: {
|
||||
fiatValue?: { data?: number; isLoading: boolean }
|
||||
fiatValue: { data?: number; isLoading: boolean }
|
||||
priceImpact?: Percent
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const priceImpactColor = useMemo(() => {
|
||||
if (!priceImpact) return undefined
|
||||
if (priceImpact.lessThan('0')) return theme.accentSuccess
|
||||
if (priceImpact.lessThan('0')) return 'accentSuccess'
|
||||
const severity = warningSeverity(priceImpact)
|
||||
if (severity < 1) return theme.textTertiary
|
||||
if (severity < 3) return theme.deprecated_yellow1
|
||||
return theme.accentFailure
|
||||
}, [priceImpact, theme.accentSuccess, theme.accentFailure, theme.textTertiary, theme.deprecated_yellow1])
|
||||
if (severity < 1) return 'textTertiary'
|
||||
if (severity < 3) return 'deprecated_yellow1'
|
||||
return 'accentFailure'
|
||||
}, [priceImpact])
|
||||
|
||||
if (fiatValue.isLoading) {
|
||||
return <FiatLoadingBubble />
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemedText.DeprecatedBody fontSize={14} color={theme.textSecondary}>
|
||||
{fiatValue?.isLoading ? (
|
||||
<FiatLoadingBubble />
|
||||
) : (
|
||||
<div>
|
||||
{fiatValue?.data ? formatNumber(fiatValue.data, NumberType.FiatTokenPrice) : undefined}
|
||||
{priceImpact && (
|
||||
<span style={{ color: priceImpactColor }}>
|
||||
{' '}
|
||||
<MouseoverTooltip text={t`The estimated difference between the USD values of input and output amounts.`}>
|
||||
(<Trans>{formatPriceImpact(priceImpact)}</Trans>)
|
||||
</MouseoverTooltip>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Row gap="sm">
|
||||
<ThemedText.BodySmall>
|
||||
{fiatValue.data ? (
|
||||
formatNumber(fiatValue.data, NumberType.FiatTokenPrice)
|
||||
) : (
|
||||
<MouseoverTooltip text={<Trans>Not enough liquidity to show accurate USD value.</Trans>}>-</MouseoverTooltip>
|
||||
)}
|
||||
</ThemedText.BodySmall>
|
||||
{priceImpact && (
|
||||
<ThemedText.BodySmall color={priceImpactColor}>
|
||||
<MouseoverTooltip
|
||||
text={<Trans>The estimated difference between the USD values of input and output amounts.</Trans>}
|
||||
>
|
||||
(<Trans>{formatPriceImpact(priceImpact)}</Trans>)
|
||||
</MouseoverTooltip>
|
||||
</ThemedText.BodySmall>
|
||||
)}
|
||||
</ThemedText.DeprecatedBody>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ interface SwapCurrencyInputPanelProps {
|
||||
pair?: Pair | null
|
||||
hideInput?: boolean
|
||||
otherCurrency?: Currency | null
|
||||
fiatValue: { data?: number; isLoading: boolean }
|
||||
fiatValue?: { data?: number; isLoading: boolean }
|
||||
priceImpact?: Percent
|
||||
id: string
|
||||
showCommonBases?: boolean
|
||||
@@ -308,7 +308,7 @@ export default function SwapCurrencyInputPanel({
|
||||
<FiatRow>
|
||||
<RowBetween>
|
||||
<LoadingOpacityContainer $loading={loading}>
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
{fiatValue && <FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />}
|
||||
</LoadingOpacityContainer>
|
||||
{account ? (
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { TraceEvent } from '@uniswap/analytics'
|
||||
import { BrowserEvent, InterfaceElementName, SwapEventName } from '@uniswap/analytics-events'
|
||||
import { Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core'
|
||||
import { Currency, CurrencyAmount } from '@uniswap/sdk-core'
|
||||
import { Pair } from '@uniswap/v2-sdk'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import { AutoColumn } from 'components/Column'
|
||||
@@ -183,7 +183,6 @@ interface CurrencyInputPanelProps {
|
||||
hideInput?: boolean
|
||||
otherCurrency?: Currency | null
|
||||
fiatValue?: { data?: number; isLoading: boolean }
|
||||
priceImpact?: Percent
|
||||
id: string
|
||||
showCommonBases?: boolean
|
||||
showCurrencyAmount?: boolean
|
||||
@@ -207,7 +206,6 @@ export default function CurrencyInputPanel({
|
||||
disableNonToken,
|
||||
renderBalance,
|
||||
fiatValue,
|
||||
priceImpact,
|
||||
hideBalance = false,
|
||||
pair = null, // used for double token logo
|
||||
hideInput = false,
|
||||
@@ -293,7 +291,7 @@ export default function CurrencyInputPanel({
|
||||
<FiatRow>
|
||||
<RowBetween>
|
||||
<LoadingOpacityContainer $loading={loading}>
|
||||
<FiatValue fiatValue={fiatValue} priceImpact={priceImpact} />
|
||||
{fiatValue && <FiatValue fiatValue={fiatValue} />}
|
||||
</LoadingOpacityContainer>
|
||||
{account ? (
|
||||
<RowFixed style={{ height: '17px' }}>
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('Expand', () => {
|
||||
Body
|
||||
</Expand>
|
||||
)
|
||||
expect(screen.queryByText('Body')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('Body')).not.toBeVisible()
|
||||
})
|
||||
|
||||
it('renders children when open', () => {
|
||||
@@ -19,7 +19,7 @@ describe('Expand', () => {
|
||||
Body
|
||||
</Expand>
|
||||
)
|
||||
expect(screen.queryByText('Body')).toBeInTheDocument()
|
||||
expect(screen.queryByText('Body')).toBeVisible()
|
||||
})
|
||||
|
||||
it('calls `onToggle` when button is pressed', () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import AnimatedDropdown from 'components/AnimatedDropdown'
|
||||
import Column from 'components/Column'
|
||||
import React, { PropsWithChildren, ReactElement } from 'react'
|
||||
import { ChevronDown } from 'react-feather'
|
||||
@@ -17,6 +18,10 @@ const ExpandIcon = styled(ChevronDown)<{ $isOpen: boolean }>`
|
||||
transition: transform ${({ theme }) => theme.transition.duration.medium};
|
||||
`
|
||||
|
||||
const Content = styled(Column)`
|
||||
padding-top: ${({ theme }) => theme.grids.md};
|
||||
`
|
||||
|
||||
export default function Expand({
|
||||
header,
|
||||
button,
|
||||
@@ -32,7 +37,7 @@ export default function Expand({
|
||||
onToggle: () => void
|
||||
}>) {
|
||||
return (
|
||||
<Column gap="md">
|
||||
<Column>
|
||||
<RowBetween>
|
||||
{header}
|
||||
<ButtonContainer data-testid={testId} onClick={onToggle} aria-expanded={isOpen}>
|
||||
@@ -40,7 +45,9 @@ export default function Expand({
|
||||
<ExpandIcon $isOpen={isOpen} />
|
||||
</ButtonContainer>
|
||||
</RowBetween>
|
||||
{isOpen && children}
|
||||
<AnimatedDropdown open={isOpen}>
|
||||
<Content gap="md">{children}</Content>
|
||||
</AnimatedDropdown>
|
||||
</Column>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -56,8 +56,8 @@ export default function FeeSelector({
|
||||
disabled?: boolean
|
||||
feeAmount?: FeeAmount
|
||||
handleFeePoolSelect: (feeAmount: FeeAmount) => void
|
||||
currencyA?: Currency | undefined
|
||||
currencyB?: Currency | undefined
|
||||
currencyA?: Currency
|
||||
currencyB?: Currency
|
||||
}) {
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ interface StepCounterProps {
|
||||
width?: string
|
||||
locked?: boolean // disable input
|
||||
title: ReactNode
|
||||
tokenA: string | undefined
|
||||
tokenB: string | undefined
|
||||
tokenA?: string
|
||||
tokenB?: string
|
||||
}
|
||||
|
||||
const StepCounter = ({
|
||||
|
||||
@@ -4,7 +4,7 @@ import styled from 'styled-components/macro'
|
||||
|
||||
import { ChartEntry } from './types'
|
||||
|
||||
const Path = styled.path<{ fill: string | undefined }>`
|
||||
const Path = styled.path<{ fill?: string }>`
|
||||
opacity: 0.5;
|
||||
stroke: ${({ fill, theme }) => fill ?? theme.accentAction};
|
||||
fill: ${({ fill, theme }) => fill ?? theme.accentAction};
|
||||
@@ -23,7 +23,7 @@ export const Area = ({
|
||||
yScale: ScaleLinear<number, number>
|
||||
xValue: (d: ChartEntry) => number
|
||||
yValue: (d: ChartEntry) => number
|
||||
fill?: string | undefined
|
||||
fill?: string
|
||||
}) =>
|
||||
useMemo(
|
||||
() => (
|
||||
|
||||
@@ -10,9 +10,9 @@ export function useDensityChartData({
|
||||
currencyB,
|
||||
feeAmount,
|
||||
}: {
|
||||
currencyA: Currency | undefined
|
||||
currencyB: Currency | undefined
|
||||
feeAmount: FeeAmount | undefined
|
||||
currencyA?: Currency
|
||||
currencyB?: Currency
|
||||
feeAmount?: FeeAmount
|
||||
}) {
|
||||
const { isLoading, error, data } = usePoolActiveLiquidity(currencyA, currencyB, feeAmount)
|
||||
|
||||
|
||||
@@ -76,11 +76,11 @@ export default function LiquidityChartRangeInput({
|
||||
onRightRangeInput,
|
||||
interactive,
|
||||
}: {
|
||||
currencyA: Currency | undefined
|
||||
currencyB: Currency | undefined
|
||||
currencyA?: Currency
|
||||
currencyB?: Currency
|
||||
feeAmount?: FeeAmount
|
||||
ticksAtLimit: { [bound in Bound]?: boolean | undefined }
|
||||
price: number | undefined
|
||||
price?: number
|
||||
priceLower?: Price<Token, Token>
|
||||
priceUpper?: Price<Token, Token>
|
||||
onLeftRangeInput: (typedValue: string) => void
|
||||
|
||||
@@ -54,7 +54,7 @@ export interface LiquidityChartRangeInputProps {
|
||||
interactive?: boolean
|
||||
|
||||
brushLabels: (d: 'w' | 'e', x: number) => string
|
||||
brushDomain: [number, number] | undefined
|
||||
brushDomain?: [number, number]
|
||||
onBrushDomainChange: (domain: [number, number], mode: string | undefined) => void
|
||||
|
||||
zoomLevels: ZoomLevels
|
||||
|
||||
@@ -39,15 +39,7 @@ export function LoadingView({ children, onDismiss }: { children: any; onDismiss:
|
||||
)
|
||||
}
|
||||
|
||||
export function SubmittedView({
|
||||
children,
|
||||
onDismiss,
|
||||
hash,
|
||||
}: {
|
||||
children: any
|
||||
onDismiss: () => void
|
||||
hash: string | undefined
|
||||
}) {
|
||||
export function SubmittedView({ children, onDismiss, hash }: { children: any; onDismiss: () => void; hash?: string }) {
|
||||
const theme = useTheme()
|
||||
const { chainId } = useWeb3React()
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ interface SearchBarDropdownSectionProps {
|
||||
suggestions: (GenieCollection | SearchToken)[]
|
||||
header: JSX.Element
|
||||
headerIcon?: JSX.Element
|
||||
hoveredIndex: number | undefined
|
||||
hoveredIndex?: number
|
||||
startingIndex: number
|
||||
setHoveredIndex: (index: number | undefined) => void
|
||||
isLoading?: boolean
|
||||
|
||||
@@ -21,7 +21,7 @@ const Tabs = styled.div`
|
||||
justify-content: space-evenly;
|
||||
`
|
||||
|
||||
const StyledHistoryLink = styled(HistoryLink)<{ flex: string | undefined }>`
|
||||
const StyledHistoryLink = styled(HistoryLink)<{ flex?: string }>`
|
||||
flex: ${({ flex }) => flex ?? 'none'};
|
||||
|
||||
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToMedium`
|
||||
@@ -64,9 +64,9 @@ export function AddRemoveTabs({
|
||||
adding: boolean
|
||||
creating: boolean
|
||||
autoSlippage: Percent
|
||||
positionID?: string | undefined
|
||||
positionID?: string
|
||||
showBackLink?: boolean
|
||||
children?: ReactNode | undefined
|
||||
children?: ReactNode
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
// reset states on back
|
||||
|
||||
@@ -53,7 +53,7 @@ export const Input = React.memo(function InnerInput({
|
||||
error?: boolean
|
||||
fontSize?: string
|
||||
align?: 'right' | 'left'
|
||||
prependSymbol?: string | undefined
|
||||
prependSymbol?: string
|
||||
} & Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'onChange' | 'as'>) {
|
||||
const enforcer = (nextUserInput: string) => {
|
||||
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
|
||||
|
||||
@@ -27,7 +27,7 @@ export const PositionPreview = ({
|
||||
position: Position
|
||||
title?: ReactNode
|
||||
inRange: boolean
|
||||
baseCurrencyDefault?: Currency | undefined
|
||||
baseCurrencyDefault?: Currency
|
||||
ticksAtLimit: { [bound: string]: boolean | undefined }
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -12,13 +12,6 @@ const renderSlippageSettings = () => {
|
||||
render(<MaxSlippageSettings autoSlippage={AUTO_SLIPPAGE} />)
|
||||
}
|
||||
|
||||
const renderAndExpandSlippageSettings = () => {
|
||||
renderSlippageSettings()
|
||||
|
||||
// By default, the button to expand Slippage component and show `input` will have `Auto` label
|
||||
fireEvent.click(screen.getByText('Auto'))
|
||||
}
|
||||
|
||||
// Switch to custom mode by tapping on `Custom` label
|
||||
const switchToCustomSlippage = () => {
|
||||
fireEvent.click(screen.getByText('Custom'))
|
||||
@@ -34,21 +27,21 @@ describe('MaxSlippageSettings', () => {
|
||||
})
|
||||
it('is not expanded by default', () => {
|
||||
renderSlippageSettings()
|
||||
expect(getSlippageInput()).not.toBeInTheDocument()
|
||||
expect(getSlippageInput()).not.toBeVisible()
|
||||
})
|
||||
it('is expanded by default when custom slippage is set', () => {
|
||||
store.dispatch(updateUserSlippageTolerance({ userSlippageTolerance: 10 }))
|
||||
renderSlippageSettings()
|
||||
expect(getSlippageInput()).toBeInTheDocument()
|
||||
expect(getSlippageInput()).toBeVisible()
|
||||
})
|
||||
it('does not render auto slippage as a value, but a placeholder', () => {
|
||||
renderAndExpandSlippageSettings()
|
||||
renderSlippageSettings()
|
||||
switchToCustomSlippage()
|
||||
|
||||
expect(getSlippageInput().value).toBe('')
|
||||
})
|
||||
it('renders custom slippage above the input', () => {
|
||||
renderAndExpandSlippageSettings()
|
||||
renderSlippageSettings()
|
||||
switchToCustomSlippage()
|
||||
|
||||
fireEvent.change(getSlippageInput(), { target: { value: '0.5' } })
|
||||
@@ -56,7 +49,7 @@ describe('MaxSlippageSettings', () => {
|
||||
expect(screen.queryAllByText('0.50%').length).toEqual(1)
|
||||
})
|
||||
it('updates input value on blur with the slippage in store', () => {
|
||||
renderAndExpandSlippageSettings()
|
||||
renderSlippageSettings()
|
||||
switchToCustomSlippage()
|
||||
|
||||
const input = getSlippageInput()
|
||||
@@ -66,7 +59,7 @@ describe('MaxSlippageSettings', () => {
|
||||
expect(input.value).toBe('0.50')
|
||||
})
|
||||
it('clears errors on blur and overwrites incorrect value with the latest correct value', () => {
|
||||
renderAndExpandSlippageSettings()
|
||||
renderSlippageSettings()
|
||||
switchToCustomSlippage()
|
||||
|
||||
const input = getSlippageInput()
|
||||
@@ -78,7 +71,7 @@ describe('MaxSlippageSettings', () => {
|
||||
expect(input.value).toBe('50.00')
|
||||
})
|
||||
it('does not allow to enter more than 2 digits after the decimal point', () => {
|
||||
renderAndExpandSlippageSettings()
|
||||
renderSlippageSettings()
|
||||
switchToCustomSlippage()
|
||||
|
||||
const input = getSlippageInput()
|
||||
@@ -88,7 +81,7 @@ describe('MaxSlippageSettings', () => {
|
||||
expect(input.value).toBe('0.01')
|
||||
})
|
||||
it('does not accept non-numerical values', () => {
|
||||
renderAndExpandSlippageSettings()
|
||||
renderSlippageSettings()
|
||||
switchToCustomSlippage()
|
||||
|
||||
const input = getSlippageInput()
|
||||
@@ -97,7 +90,7 @@ describe('MaxSlippageSettings', () => {
|
||||
expect(input.value).toBe('')
|
||||
})
|
||||
it('does not set slippage when user enters `.` value', () => {
|
||||
renderAndExpandSlippageSettings()
|
||||
renderSlippageSettings()
|
||||
switchToCustomSlippage()
|
||||
|
||||
const input = getSlippageInput()
|
||||
|
||||
@@ -9,13 +9,6 @@ const renderTransactionDeadlineSettings = () => {
|
||||
render(<TransactionDeadlineSettings />)
|
||||
}
|
||||
|
||||
const renderAndExpandTransactionDeadlineSettings = () => {
|
||||
renderTransactionDeadlineSettings()
|
||||
|
||||
// By default, the button to expand Slippage component and show `input` will have `<deadline>m` label
|
||||
fireEvent.click(screen.getByText(`${DEFAULT_DEADLINE_FROM_NOW / 60}m`))
|
||||
}
|
||||
|
||||
const getDeadlineInput = () => screen.queryByTestId('deadline-input') as HTMLInputElement
|
||||
|
||||
describe('TransactionDeadlineSettings', () => {
|
||||
@@ -26,26 +19,26 @@ describe('TransactionDeadlineSettings', () => {
|
||||
})
|
||||
it('is not expanded by default', () => {
|
||||
renderTransactionDeadlineSettings()
|
||||
expect(getDeadlineInput()).not.toBeInTheDocument()
|
||||
expect(getDeadlineInput()).not.toBeVisible()
|
||||
})
|
||||
it('is expanded by default when custom deadline is set', () => {
|
||||
store.dispatch(updateUserDeadline({ userDeadline: DEFAULT_DEADLINE_FROM_NOW * 2 }))
|
||||
renderTransactionDeadlineSettings()
|
||||
expect(getDeadlineInput()).toBeInTheDocument()
|
||||
expect(getDeadlineInput()).toBeVisible()
|
||||
})
|
||||
it('does not render default deadline as a value, but a placeholder', () => {
|
||||
renderAndExpandTransactionDeadlineSettings()
|
||||
renderTransactionDeadlineSettings()
|
||||
expect(getDeadlineInput().value).toBe('')
|
||||
})
|
||||
it('renders custom deadline above the input', () => {
|
||||
renderAndExpandTransactionDeadlineSettings()
|
||||
renderTransactionDeadlineSettings()
|
||||
|
||||
fireEvent.change(getDeadlineInput(), { target: { value: '50' } })
|
||||
|
||||
expect(screen.queryAllByText('50m').length).toEqual(1)
|
||||
})
|
||||
it('marks deadline as invalid if it is greater than 4320m (3 days) or 0m', () => {
|
||||
renderAndExpandTransactionDeadlineSettings()
|
||||
renderTransactionDeadlineSettings()
|
||||
|
||||
const input = getDeadlineInput()
|
||||
fireEvent.change(input, { target: { value: '4321' } })
|
||||
@@ -55,7 +48,7 @@ describe('TransactionDeadlineSettings', () => {
|
||||
expect(input.value).toBe('')
|
||||
})
|
||||
it('clears errors on blur and overwrites incorrect value with the latest correct value', () => {
|
||||
renderAndExpandTransactionDeadlineSettings()
|
||||
renderTransactionDeadlineSettings()
|
||||
|
||||
const input = getDeadlineInput()
|
||||
fireEvent.change(input, { target: { value: '5' } })
|
||||
@@ -69,7 +62,7 @@ describe('TransactionDeadlineSettings', () => {
|
||||
expect(input.value).toBe('5')
|
||||
})
|
||||
it('does not accept non-numerical values', () => {
|
||||
renderAndExpandTransactionDeadlineSettings()
|
||||
renderTransactionDeadlineSettings()
|
||||
|
||||
const input = getDeadlineInput()
|
||||
fireEvent.change(input, { target: { value: 'c' } })
|
||||
|
||||
@@ -68,9 +68,9 @@ const ResourcesContainer = styled.div`
|
||||
type AboutSectionProps = {
|
||||
address: string
|
||||
chainId: SupportedChainId
|
||||
description?: string | null | undefined
|
||||
homepageUrl?: string | null | undefined
|
||||
twitterName?: string | null | undefined
|
||||
description?: string | null
|
||||
homepageUrl?: string | null
|
||||
twitterName?: string | null
|
||||
}
|
||||
|
||||
export function AboutSection({ address, chainId, description, homepageUrl, twitterName }: AboutSectionProps) {
|
||||
|
||||
@@ -64,7 +64,7 @@ export function formatDelta(delta: number | null | undefined) {
|
||||
return formattedDelta
|
||||
}
|
||||
|
||||
export const DeltaText = styled.span<{ delta: number | undefined }>`
|
||||
export const DeltaText = styled.span<{ delta?: number }>`
|
||||
color: ${({ theme, delta }) =>
|
||||
delta !== undefined ? (Math.sign(delta) < 0 ? theme.accentFailure : theme.accentSuccess) : theme.textPrimary};
|
||||
`
|
||||
@@ -124,7 +124,7 @@ const timeOptionsHeight = 44
|
||||
interface PriceChartProps {
|
||||
width: number
|
||||
height: number
|
||||
prices: PricePoint[] | undefined | null
|
||||
prices?: PricePoint[] | null
|
||||
timePeriod: TimePeriod
|
||||
}
|
||||
|
||||
|
||||
@@ -87,11 +87,11 @@ function useRelevantToken(
|
||||
}
|
||||
|
||||
type TokenDetailsProps = {
|
||||
urlAddress: string | undefined
|
||||
urlAddress?: string
|
||||
inputTokenAddress?: string
|
||||
chain: Chain
|
||||
tokenQuery: TokenQuery
|
||||
tokenPriceQuery: TokenPriceQuery | undefined
|
||||
tokenPriceQuery?: TokenPriceQuery
|
||||
onChangeTimePeriod: OnChangeTimePeriod
|
||||
}
|
||||
export default function TokenDetails({
|
||||
|
||||
@@ -94,9 +94,9 @@ function TransactionSubmittedContent({
|
||||
inline,
|
||||
}: {
|
||||
onDismiss: () => void
|
||||
hash: string | undefined
|
||||
hash?: string
|
||||
chainId: number
|
||||
currencyToAdd?: Currency | undefined
|
||||
currencyToAdd?: Currency
|
||||
inline?: boolean // not in modal
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
@@ -229,9 +229,9 @@ function L2Content({
|
||||
inline,
|
||||
}: {
|
||||
onDismiss: () => void
|
||||
hash: string | undefined
|
||||
hash?: string
|
||||
chainId: SupportedL2ChainId
|
||||
currencyToAdd?: Currency | undefined
|
||||
currencyToAdd?: Currency
|
||||
pendingText: ReactNode
|
||||
inline?: boolean // not in modal
|
||||
}) {
|
||||
@@ -324,11 +324,11 @@ function L2Content({
|
||||
interface ConfirmationModalProps {
|
||||
isOpen: boolean
|
||||
onDismiss: () => void
|
||||
hash: string | undefined
|
||||
hash?: string
|
||||
content: () => ReactNode
|
||||
attemptingTxn: boolean
|
||||
pendingText: ReactNode
|
||||
currencyToAdd?: Currency | undefined
|
||||
currencyToAdd?: Currency
|
||||
}
|
||||
|
||||
export default function TransactionConfirmationModal({
|
||||
|
||||
@@ -30,15 +30,15 @@ export default function ConfirmSwapModal({
|
||||
fiatValueOutput,
|
||||
}: {
|
||||
trade: InterfaceTrade
|
||||
originalTrade: InterfaceTrade | undefined
|
||||
originalTrade?: InterfaceTrade
|
||||
attemptingTxn: boolean
|
||||
txHash: string | undefined
|
||||
txHash?: string
|
||||
allowedSlippage: Percent
|
||||
onAcceptChanges: () => void
|
||||
onConfirm: () => void
|
||||
swapErrorMessage: ReactNode | undefined
|
||||
swapErrorMessage?: ReactNode
|
||||
onDismiss: () => void
|
||||
swapQuoteReceivedDate: Date | undefined
|
||||
swapQuoteReceivedDate?: Date
|
||||
fiatValueInput: { data?: number; isLoading: boolean }
|
||||
fiatValueOutput: { data?: number; isLoading: boolean }
|
||||
}) {
|
||||
|
||||
@@ -92,7 +92,7 @@ const Wrapper = styled(Column)`
|
||||
`
|
||||
|
||||
interface SwapDetailsInlineProps {
|
||||
trade: InterfaceTrade | undefined
|
||||
trade?: InterfaceTrade
|
||||
syncing: boolean
|
||||
loading: boolean
|
||||
allowedSlippage: Percent
|
||||
|
||||
@@ -58,12 +58,12 @@ export default function SwapModalFooter({
|
||||
onAcceptChanges,
|
||||
}: {
|
||||
trade: InterfaceTrade
|
||||
hash: string | undefined
|
||||
hash?: string
|
||||
allowedSlippage: Percent
|
||||
onConfirm: () => void
|
||||
swapErrorMessage: ReactNode | undefined
|
||||
swapErrorMessage?: ReactNode
|
||||
disabledConfirm: boolean
|
||||
swapQuoteReceivedDate: Date | undefined
|
||||
swapQuoteReceivedDate?: Date
|
||||
fiatValueInput: { data?: number; isLoading: boolean }
|
||||
fiatValueOutput: { data?: number; isLoading: boolean }
|
||||
showAcceptChanges: boolean
|
||||
|
||||
@@ -33,7 +33,7 @@ interface AmountProps {
|
||||
field: Field
|
||||
tooltipText?: ReactNode
|
||||
label: ReactNode
|
||||
amount: CurrencyAmount<Currency> | undefined
|
||||
amount?: CurrencyAmount<Currency>
|
||||
usdAmount?: number
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export const PageWrapper = styled.div`
|
||||
`
|
||||
|
||||
// Mostly copied from `AppBody` but it was getting too hard to maintain backwards compatibility.
|
||||
export const SwapWrapper = styled.main<{ chainId: number | undefined }>`
|
||||
export const SwapWrapper = styled.main<{ chainId?: number }>`
|
||||
position: relative;
|
||||
background: ${({ theme }) => theme.backgroundSurface};
|
||||
border-radius: 16px;
|
||||
|
||||
@@ -37,7 +37,7 @@ const ConfirmedIcon = styled(ColumnCenter)`
|
||||
interface ExecuteModalProps {
|
||||
isOpen: boolean
|
||||
onDismiss: () => void
|
||||
proposalId: string | undefined // id for the proposal to execute
|
||||
proposalId?: string // id for the proposal to execute
|
||||
}
|
||||
|
||||
export default function ExecuteModal({ isOpen, onDismiss, proposalId }: ExecuteModalProps) {
|
||||
|
||||
@@ -37,7 +37,7 @@ const ConfirmedIcon = styled(ColumnCenter)`
|
||||
interface QueueModalProps {
|
||||
isOpen: boolean
|
||||
onDismiss: () => void
|
||||
proposalId: string | undefined // id for the proposal to queue
|
||||
proposalId?: string // id for the proposal to queue
|
||||
}
|
||||
|
||||
export default function QueueModal({ isOpen, onDismiss, proposalId }: QueueModalProps) {
|
||||
|
||||
@@ -39,8 +39,8 @@ const ConfirmedIcon = styled(ColumnCenter)`
|
||||
interface VoteModalProps {
|
||||
isOpen: boolean
|
||||
onDismiss: () => void
|
||||
voteOption: VoteOption | undefined
|
||||
proposalId: string | undefined // id for the proposal to vote on
|
||||
voteOption?: VoteOption
|
||||
proposalId?: string // id for the proposal to vote on
|
||||
}
|
||||
|
||||
export default function VoteModal({ isOpen, onDismiss, proposalId, voteOption }: VoteModalProps) {
|
||||
|
||||
@@ -140,7 +140,7 @@ export type SparklineMap = { [key: string]: PricePoint[] | undefined }
|
||||
export type TopToken = NonNullable<NonNullable<TopTokens100Query>['topTokens']>[number]
|
||||
|
||||
interface UseTopTokensReturnValue {
|
||||
tokens: TopToken[] | undefined
|
||||
tokens?: TopToken[]
|
||||
tokenSortRank: Record<string, number>
|
||||
loadingTokens: boolean
|
||||
sparklines: SparklineMap
|
||||
|
||||
@@ -153,7 +153,7 @@ export function getTokenDetailsURL({
|
||||
|
||||
export function unwrapToken<
|
||||
T extends {
|
||||
address?: string | null | undefined
|
||||
address?: string | null
|
||||
} | null
|
||||
>(chainId: number, token: T): T {
|
||||
if (!token?.address) return token
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function useFeeTierDistributionQuery(
|
||||
token0: string | undefined,
|
||||
token1: string | undefined,
|
||||
interval: number
|
||||
): { error: ApolloError | undefined; isLoading: boolean; data: FeeTierDistributionQuery } {
|
||||
): { error?: ApolloError; isLoading: boolean; data: FeeTierDistributionQuery } {
|
||||
const {
|
||||
data,
|
||||
loading: isLoading,
|
||||
|
||||
@@ -11,6 +11,9 @@ import { useClientSideV3Trade } from './useClientSideV3Trade'
|
||||
import useDebounce from './useDebounce'
|
||||
import useIsWindowVisible from './useIsWindowVisible'
|
||||
|
||||
// Prevents excessive quote requests between keystrokes.
|
||||
const DEBOUNCE_TIME = 350
|
||||
|
||||
/**
|
||||
* Returns the best v2+v3 trade for a desired swap.
|
||||
* @param tradeType whether the swap is an exact in/out
|
||||
@@ -31,7 +34,7 @@ export function useBestTrade(
|
||||
|
||||
const [debouncedAmount, debouncedOtherCurrency] = useDebounce(
|
||||
useMemo(() => [amountSpecified, otherCurrency], [amountSpecified, otherCurrency]),
|
||||
200
|
||||
DEBOUNCE_TIME
|
||||
)
|
||||
|
||||
const isAWrapTransaction = useMemo(() => {
|
||||
|
||||
@@ -33,7 +33,7 @@ export function useClientSideV3Trade<TTradeType extends TradeType>(
|
||||
tradeType: TTradeType,
|
||||
amountSpecified?: CurrencyAmount<Currency>,
|
||||
otherCurrency?: Currency
|
||||
): { state: TradeState; trade: InterfaceTrade | undefined } {
|
||||
): { state: TradeState; trade?: InterfaceTrade } {
|
||||
const [currencyIn, currencyOut] =
|
||||
tradeType === TradeType.EXACT_INPUT
|
||||
? [amountSpecified?.currency, otherCurrency]
|
||||
|
||||
@@ -5,8 +5,8 @@ import { PositionDetails } from 'types/position'
|
||||
import { useCurrency } from './Tokens'
|
||||
|
||||
export function useDerivedPositionInfo(positionDetails: PositionDetails | undefined): {
|
||||
position: Position | undefined
|
||||
pool: Pool | undefined
|
||||
position?: Position
|
||||
pool?: Pool
|
||||
} {
|
||||
const currency0 = useCurrency(positionDetails?.token0)
|
||||
const currency1 = useCurrency(positionDetails?.token1)
|
||||
|
||||
@@ -13,7 +13,7 @@ const MAX_DATA_BLOCK_AGE = 20
|
||||
interface FeeTierDistribution {
|
||||
isLoading: boolean
|
||||
isError: boolean
|
||||
largestUsageFeeTier?: FeeAmount | undefined
|
||||
largestUsageFeeTier?: FeeAmount
|
||||
|
||||
// distributions as percentages of overall liquidity
|
||||
distributions?: Record<FeeAmount, number | undefined>
|
||||
|
||||
@@ -174,7 +174,7 @@ function useAllV3Ticks(
|
||||
): {
|
||||
isLoading: boolean
|
||||
error: unknown
|
||||
ticks: TickData[] | undefined
|
||||
ticks?: TickData[]
|
||||
} {
|
||||
const useSubgraph = currencyA ? !CHAIN_IDS_MISSING_SUBGRAPH_DATA.includes(currencyA.chainId) : true
|
||||
|
||||
@@ -213,8 +213,8 @@ export function usePoolActiveLiquidity(
|
||||
): {
|
||||
isLoading: boolean
|
||||
error: any
|
||||
activeTick: number | undefined
|
||||
data: TickProcessed[] | undefined
|
||||
activeTick?: number
|
||||
data?: TickProcessed[]
|
||||
} {
|
||||
const pool = usePool(currencyA, currencyB, feeAmount)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useUniversalRouterSwapCallback } from './useUniversalRouter'
|
||||
// and the user has approved the slippage adjusted input amount for the trade
|
||||
export function useSwapCallback(
|
||||
trade: Trade<Currency, Currency, TradeType> | undefined, // trade to execute, required
|
||||
fiatValues: { amountIn: number | undefined; amountOut: number | undefined }, // usd values for amount in and out, logged for analytics
|
||||
fiatValues: { amountIn?: number; amountOut?: number }, // usd values for amount in and out, logged for analytics
|
||||
allowedSlippage: Percent, // in bips
|
||||
permitSignature: PermitSignature | undefined
|
||||
): { callback: null | (() => Promise<string>) } {
|
||||
|
||||
@@ -11,7 +11,7 @@ export function useTokenAllowance(
|
||||
owner?: string,
|
||||
spender?: string
|
||||
): {
|
||||
tokenAllowance: CurrencyAmount<Token> | undefined
|
||||
tokenAllowance?: CurrencyAmount<Token>
|
||||
isSyncing: boolean
|
||||
} {
|
||||
const contract = useTokenContract(token?.address, false)
|
||||
@@ -21,7 +21,7 @@ export function useTokenAllowance(
|
||||
// This guarantees that the tokenAllowance is marked isSyncing upon approval and updated upon being synced.
|
||||
const [blocksPerFetch, setBlocksPerFetch] = useState<1>()
|
||||
const { result, syncing: isSyncing } = useSingleCallResult(contract, 'allowance', inputs, { blocksPerFetch }) as {
|
||||
result: Awaited<ReturnType<NonNullable<typeof contract>['allowance']>> | undefined
|
||||
result?: Awaited<ReturnType<NonNullable<typeof contract>['allowance']>>
|
||||
syncing: boolean
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const ETH_AMOUNT_OUT: { [chainId: number]: CurrencyAmount<Currency> } = {
|
||||
}
|
||||
|
||||
function useETHValue(currencyAmount?: CurrencyAmount<Currency>): {
|
||||
data: CurrencyAmount<Currency> | undefined
|
||||
data?: CurrencyAmount<Currency>
|
||||
isLoading: boolean
|
||||
} {
|
||||
const chainId = currencyAmount?.currency?.chainId
|
||||
@@ -50,8 +50,11 @@ function useETHValue(currencyAmount?: CurrencyAmount<Currency>): {
|
||||
return { data: price.quote(currencyAmount), isLoading: false }
|
||||
}
|
||||
|
||||
// TODO(WEB-2095): This hook should early return `null` when `currencyAmount` is undefined. Otherwise,
|
||||
// it is not possible to differentiate between a loading state and a state where `currencyAmount`
|
||||
// is undefined
|
||||
export function useUSDPrice(currencyAmount?: CurrencyAmount<Currency>): {
|
||||
data: number | undefined
|
||||
data?: number
|
||||
isLoading: boolean
|
||||
} {
|
||||
const chain = currencyAmount?.currency.chainId ? chainIdToBackendName(currencyAmount?.currency.chainId) : undefined
|
||||
|
||||
@@ -45,7 +45,7 @@ interface SwapOptions {
|
||||
|
||||
export function useUniversalRouterSwapCallback(
|
||||
trade: Trade<Currency, Currency, TradeType> | undefined,
|
||||
fiatValues: { amountIn: number | undefined; amountOut: number | undefined },
|
||||
fiatValues: { amountIn?: number; amountOut?: number },
|
||||
options: SwapOptions
|
||||
) {
|
||||
const { account, chainId, provider } = useWeb3React()
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useV3NFTPositionManagerContract } from './useContract'
|
||||
|
||||
interface UseV3PositionsResults {
|
||||
loading: boolean
|
||||
positions: PositionDetails[] | undefined
|
||||
positions?: PositionDetails[]
|
||||
}
|
||||
|
||||
function useV3PositionsFromTokenIds(tokenIds: BigNumber[] | undefined): UseV3PositionsResults {
|
||||
@@ -51,7 +51,7 @@ function useV3PositionsFromTokenIds(tokenIds: BigNumber[] | undefined): UseV3Pos
|
||||
|
||||
interface UseV3PositionResults {
|
||||
loading: boolean
|
||||
position: PositionDetails | undefined
|
||||
position?: PositionDetails
|
||||
}
|
||||
|
||||
export function useV3PositionFromTokenId(tokenId: BigNumber | undefined): UseV3PositionResults {
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function useWrapCallback(
|
||||
inputCurrency: Currency | undefined | null,
|
||||
outputCurrency: Currency | undefined | null,
|
||||
typedValue: string | undefined
|
||||
): { wrapType: WrapType; execute?: undefined | (() => Promise<void>); inputError?: WrapInputError } {
|
||||
): { wrapType: WrapType; execute?: () => Promise<void>; inputError?: WrapInputError } {
|
||||
const { chainId, account } = useWeb3React()
|
||||
const wethContract = useWETHContract()
|
||||
const balance = useCurrencyBalance(account ?? undefined, inputCurrency ?? undefined)
|
||||
|
||||
@@ -15,9 +15,9 @@ export function useRoutingAPIArguments({
|
||||
tradeType,
|
||||
routerPreference,
|
||||
}: {
|
||||
tokenIn: Currency | undefined
|
||||
tokenOut: Currency | undefined
|
||||
amount: CurrencyAmount<Currency> | undefined
|
||||
tokenIn?: Currency
|
||||
tokenOut?: Currency
|
||||
amount?: CurrencyAmount<Currency>
|
||||
tradeType: TradeType
|
||||
routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE
|
||||
}) {
|
||||
|
||||
@@ -40,7 +40,7 @@ export const formatSwapSignedAnalyticsEventProperties = ({
|
||||
txHash,
|
||||
}: {
|
||||
trade: InterfaceTrade | Trade<Currency, Currency, TradeType>
|
||||
fiatValues: { amountIn: number | undefined; amountOut: number | undefined }
|
||||
fiatValues: { amountIn?: number; amountOut?: number }
|
||||
txHash: string
|
||||
}) => ({
|
||||
transaction_hash: txHash,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { DEFAULT_LOCALE, SUPPORTED_LOCALES } from 'constants/locales'
|
||||
|
||||
interface FormatLocaleNumberArgs {
|
||||
number: CurrencyAmount<Currency> | Price<Currency, Currency> | number
|
||||
locale: string | null | undefined
|
||||
locale?: string | null
|
||||
options?: Intl.NumberFormatOptions
|
||||
sigFigs?: number
|
||||
fixedDecimals?: number
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
const ENS_NAME_REGEX = /^(([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+)eth(\/.*)?$/
|
||||
|
||||
export default function parseENSAddress(
|
||||
ensAddress: string
|
||||
): { ensName: string; ensPath: string | undefined } | undefined {
|
||||
export default function parseENSAddress(ensAddress: string): { ensName: string; ensPath?: string } | undefined {
|
||||
const match = ensAddress.match(ENS_NAME_REGEX)
|
||||
if (!match) return undefined
|
||||
return { ensName: `${match[1].toLowerCase()}eth`, ensPath: match[4] }
|
||||
|
||||
@@ -206,9 +206,9 @@ const InputCurrencyValue = ({
|
||||
}: {
|
||||
usingPayWithAnyToken: boolean
|
||||
totalEthPrice: BigNumber
|
||||
activeCurrency: Currency | undefined | null
|
||||
activeCurrency?: Currency | null
|
||||
tradeState: TradeState
|
||||
trade: InterfaceTrade | undefined
|
||||
trade?: InterfaceTrade
|
||||
}) => {
|
||||
if (!usingPayWithAnyToken) {
|
||||
return (
|
||||
@@ -241,7 +241,7 @@ const FiatValue = ({
|
||||
usingPayWithAnyToken,
|
||||
}: {
|
||||
usdcValue: CurrencyAmount<Token> | null
|
||||
priceImpact: PriceImpact | undefined
|
||||
priceImpact?: PriceImpact
|
||||
tradeState: TradeState
|
||||
usingPayWithAnyToken: boolean
|
||||
}) => {
|
||||
|
||||
@@ -82,7 +82,7 @@ const NoContentContainer = () => (
|
||||
|
||||
interface BagRowProps {
|
||||
asset: UpdatedGenieAsset
|
||||
usdPrice: number | undefined
|
||||
usdPrice?: number
|
||||
removeAsset: (assets: GenieAsset[]) => void
|
||||
showRemove?: boolean
|
||||
grayscale?: boolean
|
||||
@@ -168,7 +168,7 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is
|
||||
|
||||
interface PriceChangeBagRowProps {
|
||||
asset: UpdatedGenieAsset
|
||||
usdPrice: number | undefined
|
||||
usdPrice?: number
|
||||
markAssetAsReviewed: (asset: UpdatedGenieAsset, toKeep: boolean) => void
|
||||
top?: boolean
|
||||
isMobile: boolean
|
||||
@@ -219,7 +219,7 @@ export const PriceChangeBagRow = ({ asset, usdPrice, markAssetAsReviewed, top, i
|
||||
|
||||
interface UnavailableAssetsHeaderRowProps {
|
||||
assets?: UpdatedGenieAsset[]
|
||||
usdPrice: number | undefined
|
||||
usdPrice?: number
|
||||
clearUnavailableAssets: () => void
|
||||
didOpenUnavailableAssets: boolean
|
||||
setDidOpenUnavailableAssets: (didOpen: boolean) => void
|
||||
|
||||
@@ -111,7 +111,7 @@ const NftDisplayContainer = styled.div`
|
||||
height: 34px;
|
||||
`
|
||||
|
||||
const NftHolder = styled.div<{ index: number; src: string | undefined }>`
|
||||
const NftHolder = styled.div<{ index: number; src?: string }>`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
|
||||
@@ -147,7 +147,7 @@ export const LoadingAssetActivity = ({ rowCount }: { rowCount: number }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const AssetActivity = ({ events }: { events: ActivityEvent[] | undefined }) => {
|
||||
const AssetActivity = ({ events }: { events?: ActivityEvent[] }) => {
|
||||
return (
|
||||
<ActivityTable>
|
||||
{events &&
|
||||
|
||||
@@ -128,7 +128,7 @@ function assetMediaType(asset: GenieAsset): MediaType {
|
||||
return MediaType.Image
|
||||
}
|
||||
|
||||
const RenderMediaShadow = ({ imageUrl }: { imageUrl: string | undefined }) => {
|
||||
const RenderMediaShadow = ({ imageUrl }: { imageUrl?: string }) => {
|
||||
const [contentNotAvailable, setContentNotAvailable] = useState(false)
|
||||
|
||||
if (!imageUrl || contentNotAvailable) {
|
||||
|
||||
@@ -32,7 +32,7 @@ const InputWrapper = styled(Row)<{ borderColor: string }>`
|
||||
box-sizing: border-box;
|
||||
`
|
||||
|
||||
const CurrencyWrapper = styled.div<{ listPrice: number | undefined }>`
|
||||
const CurrencyWrapper = styled.div<{ listPrice?: number }>`
|
||||
color: ${({ listPrice, theme }) => (listPrice ? theme.textPrimary : theme.textSecondary)};
|
||||
`
|
||||
|
||||
|
||||
@@ -313,7 +313,7 @@ const CollectionFilterItem = ({
|
||||
collection,
|
||||
setCollectionFilters,
|
||||
}: {
|
||||
collection: WalletCollection | undefined
|
||||
collection?: WalletCollection
|
||||
setCollectionFilters: (address: string) => void
|
||||
}) => {
|
||||
if (!collection) return null
|
||||
|
||||
@@ -9,8 +9,8 @@ export default function useDerivedPayWithAnyTokenSwapInfo(
|
||||
parsedOutputAmount?: CurrencyAmount<NativeCurrency | Token>
|
||||
): {
|
||||
state: TradeState
|
||||
trade: InterfaceTrade | undefined
|
||||
maximumAmountIn: CurrencyAmount<Token> | undefined
|
||||
trade?: InterfaceTrade
|
||||
maximumAmountIn?: CurrencyAmount<Token>
|
||||
allowedSlippage: Percent
|
||||
} {
|
||||
const { state, trade } = useBestTrade(TradeType.EXACT_OUTPUT, parsedOutputAmount, inputCurrency ?? undefined)
|
||||
|
||||
@@ -4,10 +4,10 @@ import { create } from 'zustand'
|
||||
import { devtools } from 'zustand/middleware'
|
||||
|
||||
interface TokenInputState {
|
||||
inputCurrency: Currency | undefined
|
||||
inputCurrency?: Currency
|
||||
setInputCurrency: (currency: Currency | undefined) => void
|
||||
clearInputCurrency: () => void
|
||||
tokenTradeInput: TokenTradeInput | undefined
|
||||
tokenTradeInput?: TokenTradeInput
|
||||
setTokenTradeInput: (tokenTradeInput: TokenTradeInput | undefined) => void
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ interface WalletBalanceProps {
|
||||
address: string
|
||||
balance: string
|
||||
weiBalance: BigNumber
|
||||
provider: Web3Provider | undefined
|
||||
provider?: Web3Provider
|
||||
}
|
||||
|
||||
export function useWalletBalance(): WalletBalanceProps {
|
||||
|
||||
@@ -88,7 +88,7 @@ export enum ListingStatus {
|
||||
}
|
||||
|
||||
export interface AssetRow {
|
||||
image: string | undefined
|
||||
image?: string
|
||||
name?: string
|
||||
status: ListingStatus
|
||||
marketplace: ListingMarket
|
||||
|
||||
@@ -109,9 +109,9 @@ function buildTradeRouteInput(swap: Swap): TokenTradeRouteInput {
|
||||
}
|
||||
|
||||
export function buildAllTradeRouteInputs(trade: InterfaceTrade): {
|
||||
mixedTokenTradeRouteInputs: TokenTradeRouteInput[] | undefined
|
||||
v2TokenTradeRouteInputs: TokenTradeRouteInput[] | undefined
|
||||
v3TokenTradeRouteInputs: TokenTradeRouteInput[] | undefined
|
||||
mixedTokenTradeRouteInputs?: TokenTradeRouteInput[]
|
||||
v2TokenTradeRouteInputs?: TokenTradeRouteInput[]
|
||||
v3TokenTradeRouteInputs?: TokenTradeRouteInput[]
|
||||
} {
|
||||
const mixedTokenTradeRouteInputs: TokenTradeRouteInput[] = []
|
||||
const v2TokenTradeRouteInputs: TokenTradeRouteInput[] = []
|
||||
|
||||
@@ -31,7 +31,7 @@ export const ProposalActionDetail = ({
|
||||
}: {
|
||||
className?: string
|
||||
proposalAction: ProposalAction
|
||||
currency: Currency | undefined
|
||||
currency?: Currency
|
||||
amount: string
|
||||
toAddress: string
|
||||
onCurrencySelect: (currency: Currency) => void
|
||||
|
||||
@@ -15,7 +15,7 @@ export const ProposalSubmissionModal = ({
|
||||
onDismiss,
|
||||
}: {
|
||||
isOpen: boolean
|
||||
hash: string | undefined
|
||||
hash?: string
|
||||
onDismiss: () => void
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
@@ -172,7 +172,7 @@ export function Swap({
|
||||
}: {
|
||||
className?: string
|
||||
prefilledState?: Partial<SwapState>
|
||||
chainId: SupportedChainId | undefined
|
||||
chainId?: SupportedChainId
|
||||
onCurrencyChange?: (selected: Pick<SwapState, Field.INPUT | Field.OUTPUT>) => void
|
||||
disableTokenInputs?: boolean
|
||||
}) {
|
||||
@@ -287,8 +287,11 @@ export function Swap({
|
||||
},
|
||||
[independentField, parsedAmount, showWrap, trade]
|
||||
)
|
||||
|
||||
const fiatValueInput = useUSDPrice(parsedAmounts[Field.INPUT])
|
||||
const fiatValueOutput = useUSDPrice(parsedAmounts[Field.OUTPUT])
|
||||
const showFiatValueInput = Boolean(parsedAmounts[Field.INPUT])
|
||||
const showFiatValueOutput = Boolean(parsedAmounts[Field.OUTPUT])
|
||||
|
||||
const [routeNotFound, routeIsLoading, routeIsSyncing] = useMemo(
|
||||
() => [!trade?.swaps, TradeState.LOADING === tradeState, TradeState.LOADING === tradeState && Boolean(trade)],
|
||||
@@ -334,10 +337,10 @@ export function Swap({
|
||||
// modal and loading
|
||||
const [{ showConfirm, tradeToConfirm, swapErrorMessage, attemptingTxn, txHash }, setSwapState] = useState<{
|
||||
showConfirm: boolean
|
||||
tradeToConfirm: InterfaceTrade | undefined
|
||||
tradeToConfirm?: InterfaceTrade
|
||||
attemptingTxn: boolean
|
||||
swapErrorMessage: string | undefined
|
||||
txHash: string | undefined
|
||||
swapErrorMessage?: string
|
||||
txHash?: string
|
||||
}>({
|
||||
showConfirm: false,
|
||||
tradeToConfirm: undefined,
|
||||
@@ -580,10 +583,10 @@ export function Swap({
|
||||
currency={currencies[Field.INPUT] ?? null}
|
||||
onUserInput={handleTypeInput}
|
||||
onMax={handleMaxInput}
|
||||
fiatValue={fiatValueInput}
|
||||
fiatValue={showFiatValueInput ? fiatValueInput : undefined}
|
||||
onCurrencySelect={handleInputSelect}
|
||||
otherCurrency={currencies[Field.OUTPUT]}
|
||||
showCommonBases={true}
|
||||
showCommonBases
|
||||
id={InterfaceSectionName.CURRENCY_INPUT_PANEL}
|
||||
loading={independentField === Field.OUTPUT && routeIsSyncing}
|
||||
/>
|
||||
@@ -621,12 +624,12 @@ export function Swap({
|
||||
label={independentField === Field.INPUT && !showWrap ? <Trans>To (at least)</Trans> : <Trans>To</Trans>}
|
||||
showMaxButton={false}
|
||||
hideBalance={false}
|
||||
fiatValue={fiatValueOutput}
|
||||
fiatValue={showFiatValueOutput ? fiatValueOutput : undefined}
|
||||
priceImpact={stablecoinPriceImpact}
|
||||
currency={currencies[Field.OUTPUT] ?? null}
|
||||
onCurrencySelect={handleOutputSelect}
|
||||
otherCurrency={currencies[Field.INPUT]}
|
||||
showCommonBases={true}
|
||||
showCommonBases
|
||||
id={InterfaceSectionName.CURRENCY_OUTPUT_PANEL}
|
||||
loading={independentField === Field.INPUT && routeIsSyncing}
|
||||
/>
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { createPopper } from '@popperjs/core'
|
||||
import { useWeb3React } from '@web3-react/core'
|
||||
import failOnConsole from 'jest-fail-on-console'
|
||||
import { Readable } from 'stream'
|
||||
import { toBeVisible } from 'test-utils/matchers'
|
||||
import { mocked } from 'test-utils/mocked'
|
||||
import { TextDecoder, TextEncoder } from 'util'
|
||||
|
||||
@@ -100,3 +101,7 @@ failOnConsole({
|
||||
shouldFailOnLog: true,
|
||||
shouldFailOnWarn: true,
|
||||
})
|
||||
|
||||
expect.extend({
|
||||
toBeVisible,
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ const connectionSlice = createSlice({
|
||||
reducers: {
|
||||
updateConnectionError(
|
||||
state,
|
||||
{ payload: { connectionType, error } }: { payload: { connectionType: ConnectionType; error: string | undefined } }
|
||||
{ payload: { connectionType, error } }: { payload: { connectionType: ConnectionType; error?: string } }
|
||||
) {
|
||||
state.errorByConnectionType[connectionType] = error
|
||||
},
|
||||
|
||||
@@ -362,7 +362,7 @@ export function useUserDelegatee(): string {
|
||||
}
|
||||
|
||||
// gets the users current votes
|
||||
export function useUserVotes(): { loading: boolean; votes: CurrencyAmount<Token> | undefined } {
|
||||
export function useUserVotes(): { loading: boolean; votes?: CurrencyAmount<Token> } {
|
||||
const { account, chainId } = useWeb3React()
|
||||
const uniContract = useUniContract()
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ enum LogsState {
|
||||
}
|
||||
|
||||
interface UseLogsResult {
|
||||
logs: Log[] | undefined
|
||||
logs?: Log[]
|
||||
state: LogsState
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ export function useV3DerivedMintInfo(
|
||||
currencyBalances: { [field in Field]?: CurrencyAmount<Currency> }
|
||||
dependentField: Field
|
||||
parsedAmounts: { [field in Field]?: CurrencyAmount<Currency> }
|
||||
position: Position | undefined
|
||||
position?: Position
|
||||
noLiquidity?: boolean
|
||||
errorMessage?: ReactNode
|
||||
invalidPool: boolean
|
||||
|
||||
@@ -58,7 +58,7 @@ interface StakingInfo {
|
||||
// equivalent to percent of total supply * reward rate
|
||||
rewardRate: CurrencyAmount<Token>
|
||||
// when the period ends
|
||||
periodFinish: Date | undefined
|
||||
periodFinish?: Date
|
||||
// if pool is active
|
||||
active: boolean
|
||||
// calculates a hypothetical amount of token distributed to the active account per second.
|
||||
|
||||
@@ -78,7 +78,7 @@ export function useDerivedSwapInfo(
|
||||
): {
|
||||
currencies: { [field in Field]?: Currency | null }
|
||||
currencyBalances: { [field in Field]?: CurrencyAmount<Currency> }
|
||||
parsedAmount: CurrencyAmount<Currency> | undefined
|
||||
parsedAmount?: CurrencyAmount<Currency>
|
||||
inputError?: ReactNode
|
||||
trade: {
|
||||
trade?: InterfaceTrade
|
||||
|
||||
@@ -8,10 +8,10 @@ export interface SwapState {
|
||||
readonly independentField: Field
|
||||
readonly typedValue: string
|
||||
readonly [Field.INPUT]: {
|
||||
readonly currencyId: string | undefined | null
|
||||
readonly currencyId?: string | null
|
||||
}
|
||||
readonly [Field.OUTPUT]: {
|
||||
readonly currencyId: string | undefined | null
|
||||
readonly currencyId?: string | null
|
||||
}
|
||||
// the typed recipient address or ENS name, or null if swap should go to sender
|
||||
readonly recipient: string | null
|
||||
|
||||
@@ -10,7 +10,7 @@ import { SerializedPair, SerializedToken, SlippageTolerance } from './types'
|
||||
const currentTimestamp = () => new Date().getTime()
|
||||
|
||||
export interface UserState {
|
||||
buyFiatFlowCompleted: boolean | undefined
|
||||
buyFiatFlowCompleted?: boolean
|
||||
|
||||
selectedWallet?: ConnectionType
|
||||
|
||||
@@ -53,7 +53,7 @@ export interface UserState {
|
||||
URLWarningVisible: boolean
|
||||
hideUniswapWalletBanner: boolean
|
||||
// undefined means has not gone through A/B split yet
|
||||
showSurveyPopup: boolean | undefined
|
||||
showSurveyPopup?: boolean
|
||||
}
|
||||
|
||||
function pairKey(token0Address: string, token1Address: string) {
|
||||
|
||||
22
src/test-utils/matchers.test.tsx
Normal file
22
src/test-utils/matchers.test.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { render, screen } from './render'
|
||||
|
||||
describe('matchers', () => {
|
||||
describe('toBeVisible', () => {
|
||||
it('should return true if element is visible', () => {
|
||||
render(<div>test</div>)
|
||||
expect(screen.getByText('test')).toBeVisible()
|
||||
})
|
||||
it('should return false if element is hidden', () => {
|
||||
render(<div style={{ height: 0 }}>test</div>)
|
||||
expect(screen.getByText('test')).not.toBeVisible()
|
||||
})
|
||||
it('should return false if parent element is hidden', () => {
|
||||
render(
|
||||
<div style={{ height: 0 }}>
|
||||
<div>test</div>
|
||||
</div>
|
||||
)
|
||||
expect(screen.getByText('test')).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
32
src/test-utils/matchers.ts
Normal file
32
src/test-utils/matchers.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
// This type is not exported from Jest, so we need to infer it from the expect.extend function.
|
||||
type MatcherFunction = Parameters<typeof expect.extend>[0] extends { [key: string]: infer I } ? I : never
|
||||
|
||||
const isElementVisible = (element: HTMLElement): boolean => {
|
||||
return element.style.height !== '0px' && (!element.parentElement || isElementVisible(element.parentElement))
|
||||
}
|
||||
|
||||
// Overrides the Testing Library matcher to check for height when determining whether an element is visible.
|
||||
// We are doing this because:
|
||||
// - original `toBeVisible()` does not take `height` into account
|
||||
// https://github.com/testing-library/jest-dom/issues/450
|
||||
// - original `toBeVisible()` and `toHaveStyle()` does not work at all in some cases
|
||||
// https://github.com/testing-library/jest-dom/issues/209
|
||||
// - `getComputedStyles()` returns empty object, making it impossible to check for Styled Components styles
|
||||
// https://github.com/styled-components/styled-components/issues/3262
|
||||
// https://github.com/jsdom/jsdom/issues/2986
|
||||
// For the reasons above, this matcher only works for inline styles.
|
||||
export const toBeVisible: MatcherFunction = function (element: HTMLElement) {
|
||||
const isVisible = isElementVisible(element)
|
||||
return {
|
||||
pass: isVisible,
|
||||
message: () => {
|
||||
const is = isVisible ? 'is' : 'is not'
|
||||
return [
|
||||
this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeVisible`, 'element', ''),
|
||||
'',
|
||||
`Received element ${is} visible:`,
|
||||
` ${this.utils.printReceived(element.cloneNode(false))}`,
|
||||
].join('\n')
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export const priceToPreciseFloat = (price: Price<Currency, Currency> | undefined
|
||||
}
|
||||
|
||||
interface FormatDollarArgs {
|
||||
num: number | undefined | null
|
||||
num?: number | null
|
||||
isPrice?: boolean
|
||||
lessPreciseStablecoinValues?: boolean
|
||||
digits?: number
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Price, Token } from '@uniswap/sdk-core'
|
||||
import { Bound } from '../state/mint/v3/actions'
|
||||
|
||||
interface FormatTickPriceArgs {
|
||||
price: Price<Token, Token> | undefined
|
||||
price?: Price<Token, Token>
|
||||
atLimit: { [bound in Bound]?: boolean | undefined }
|
||||
direction: Bound
|
||||
placeholder?: string
|
||||
|
||||
@@ -57,12 +57,12 @@ export const formatSwapPriceUpdatedEventProperties = (
|
||||
|
||||
interface AnalyticsEventProps {
|
||||
trade: InterfaceTrade
|
||||
hash: string | undefined
|
||||
hash?: string
|
||||
allowedSlippage: Percent
|
||||
transactionDeadlineSecondsSinceEpoch: number | undefined
|
||||
transactionDeadlineSecondsSinceEpoch?: number
|
||||
isAutoSlippage: boolean
|
||||
isAutoRouterApi: boolean
|
||||
swapQuoteReceivedDate: Date | undefined
|
||||
swapQuoteReceivedDate?: Date
|
||||
routes: RoutingDiagramEntry[]
|
||||
fiatValueInput?: number
|
||||
fiatValueOutput?: number
|
||||
|
||||
@@ -2,8 +2,11 @@ import { UAParser } from 'ua-parser-js'
|
||||
|
||||
const parser = new UAParser(window.navigator.userAgent)
|
||||
const { type } = parser.getDevice()
|
||||
const { name } = parser.getBrowser()
|
||||
|
||||
export const isMobile = type === 'mobile' || type === 'tablet'
|
||||
const platform = parser.getOS().name
|
||||
export const isIOS = platform === 'iOS'
|
||||
export const isNonIOSPhone = !isIOS && type === 'mobile'
|
||||
|
||||
export const isMobileSafari = isMobile && isIOS && name?.toLowerCase().includes('safari')
|
||||
|
||||
11
yarn.lock
11
yarn.lock
@@ -5158,9 +5158,9 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311"
|
||||
integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==
|
||||
|
||||
"@types/ua-parser-js@^0.7.35":
|
||||
"@types/ua-parser-js@^0.7.36":
|
||||
version "0.7.36"
|
||||
resolved "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz#9bd0b47f26b5a3151be21ba4ce9f5fa457c5f190"
|
||||
integrity sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==
|
||||
|
||||
"@types/use-sync-external-store@^0.0.3":
|
||||
@@ -18151,11 +18151,16 @@ typical@^2.6.0, typical@^2.6.1:
|
||||
resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d"
|
||||
integrity sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=
|
||||
|
||||
ua-parser-js@^0.7.28, ua-parser-js@^0.7.30:
|
||||
ua-parser-js@^0.7.30:
|
||||
version "0.7.31"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
|
||||
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
|
||||
|
||||
ua-parser-js@^1.0.35:
|
||||
version "1.0.35"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.35.tgz#c4ef44343bc3db0a3cbefdf21822f1b1fc1ab011"
|
||||
integrity sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA==
|
||||
|
||||
ufo@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.1.tgz#e70265e7152f3aba425bd013d150b2cdf4056d7c"
|
||||
|
||||
Reference in New Issue
Block a user