Compare commits

...

7 Commits

Author SHA1 Message Date
Jordan Frankfurt
d23b6e5da6 fix: remove app advert on mobile safari (#6630)
Co-authored-by: Jordan Frankfurt <jordan@CORN-Jordan-949.frankfurt>
2023-05-25 10:36:57 -05:00
Zach Pomerantz
44c355c7f0 build: caching i18n extractor (#6619)
* fix: do not attempt to cache i18n:extract

* fix: i18n extraction

* docs: improve comments
2023-05-24 14:23:40 -07:00
Vignesh Mohankumar
e4a9764a12 fix: increase useBestTrade debounce time (#6631)
* fix: increase useBestTrade debounce time

* reduce

* increase

* 350

* fix
2023-05-24 14:44:18 -04:00
Mike Grabowski
303fa15240 feat: show affordance in swap UI when we can't fetch usd quote (#6622)
* initial commit:

* add todo to linear
2023-05-24 22:32:53 +04:00
eddie
d180aef306 fix: no-undefined-or in object field types (#6640) 2023-05-24 11:31:40 -07:00
Mike Grabowski
c07c401189 feat: add animation to Settings menu (#6617)
* feat: add price impact back

* chore: update tes tname

* chore: update snapshot for price impact

* fix

* fix

* update snapshot after rebase

* update snapshot

* chore: finish

* chore: remove snapshot

* feat: add test matcher

* cleanup

* chore: add animation test

* add comment

* update comment
2023-05-24 22:02:59 +04:00
Zach Pomerantz
65d91eb363 build: use repository slack secret (#6639) 2023-05-24 13:15:28 -04:00
92 changed files with 425 additions and 237 deletions

View File

@@ -14,6 +14,7 @@ module.exports = {
rules: {
'multiline-comment-style': ['error', 'separate-lines'],
'rulesdir/enforce-retry-on-import': 'error',
'rulesdir/no-undefined-or': 'error',
},
},
{

View File

@@ -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

View File

@@ -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:

View 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)
},
})
}
},
}
},
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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.

View 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()
})
})
})

View File

@@ -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>
)

View File

@@ -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>

View File

@@ -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

View File

@@ -19,7 +19,7 @@ interface SparklineChartProps {
width: number
height: number
tokenData: TopToken
pricePercentChange: number | undefined | null
pricePercentChange?: number | null
sparklineMap: SparklineMap
}

View File

@@ -14,7 +14,7 @@ const ContentWrapper = styled(Column)`
font-size: 12px;
`
interface ConnectedAccountBlockedProps {
account: string | null | undefined
account?: string | null
isOpen: boolean
}

View File

@@ -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>
)
}

View File

@@ -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' }}>

View File

@@ -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' }}>

View File

@@ -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', () => {

View File

@@ -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>
)
}

View File

@@ -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()

View File

@@ -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 = ({

View File

@@ -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(
() => (

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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))) {

View File

@@ -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()

View File

@@ -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()

View File

@@ -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' } })

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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({

View File

@@ -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({

View File

@@ -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 }
}) {

View File

@@ -92,7 +92,7 @@ const Wrapper = styled(Column)`
`
interface SwapDetailsInlineProps {
trade: InterfaceTrade | undefined
trade?: InterfaceTrade
syncing: boolean
loading: boolean
allowedSlippage: Percent

View File

@@ -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

View File

@@ -33,7 +33,7 @@ interface AmountProps {
field: Field
tooltipText?: ReactNode
label: ReactNode
amount: CurrencyAmount<Currency> | undefined
amount?: CurrencyAmount<Currency>
usdAmount?: number
}

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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(() => {

View File

@@ -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]

View File

@@ -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)

View File

@@ -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>

View File

@@ -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)

View File

@@ -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>) } {

View File

@@ -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
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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
}) {

View File

@@ -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,

View File

@@ -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

View File

@@ -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] }

View File

@@ -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
}) => {

View File

@@ -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

View File

@@ -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%;

View File

@@ -147,7 +147,7 @@ export const LoadingAssetActivity = ({ rowCount }: { rowCount: number }) => {
)
}
const AssetActivity = ({ events }: { events: ActivityEvent[] | undefined }) => {
const AssetActivity = ({ events }: { events?: ActivityEvent[] }) => {
return (
<ActivityTable>
{events &&

View File

@@ -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) {

View File

@@ -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)};
`

View File

@@ -313,7 +313,7 @@ const CollectionFilterItem = ({
collection,
setCollectionFilters,
}: {
collection: WalletCollection | undefined
collection?: WalletCollection
setCollectionFilters: (address: string) => void
}) => {
if (!collection) return null

View File

@@ -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)

View File

@@ -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
}

View File

@@ -8,7 +8,7 @@ interface WalletBalanceProps {
address: string
balance: string
weiBalance: BigNumber
provider: Web3Provider | undefined
provider?: Web3Provider
}
export function useWalletBalance(): WalletBalanceProps {

View File

@@ -88,7 +88,7 @@ export enum ListingStatus {
}
export interface AssetRow {
image: string | undefined
image?: string
name?: string
status: ListingStatus
marketplace: ListingMarket

View File

@@ -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[] = []

View File

@@ -31,7 +31,7 @@ export const ProposalActionDetail = ({
}: {
className?: string
proposalAction: ProposalAction
currency: Currency | undefined
currency?: Currency
amount: string
toAddress: string
onCurrencySelect: (currency: Currency) => void

View File

@@ -15,7 +15,7 @@ export const ProposalSubmissionModal = ({
onDismiss,
}: {
isOpen: boolean
hash: string | undefined
hash?: string
onDismiss: () => void
}) => {
const theme = useTheme()

View File

@@ -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}
/>

View File

@@ -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,
})

View File

@@ -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
},

View File

@@ -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()

View File

@@ -21,7 +21,7 @@ enum LogsState {
}
interface UseLogsResult {
logs: Log[] | undefined
logs?: Log[]
state: LogsState
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View 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()
})
})
})

View 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')
},
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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')

View File

@@ -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"