diff --git a/src/components/sharedComponents/BigNumberInput.tsx b/src/components/sharedComponents/BigNumberInput.tsx index 60702ede..d299629e 100644 --- a/src/components/sharedComponents/BigNumberInput.tsx +++ b/src/components/sharedComponents/BigNumberInput.tsx @@ -6,6 +6,7 @@ import { type RefObject, useEffect, useRef, + useState, } from 'react' import { formatUnits, maxUint256, parseUnits } from 'viem' export type RenderInputProps = Omit & { @@ -66,6 +67,7 @@ export const BigNumberInput: FC = ({ value, }: BigNumberInputProps) => { const inputRef = useRef(null) + const [hasError, setHasError] = useState(false) // update inputValue when value changes useEffect(() => { @@ -73,7 +75,15 @@ export const BigNumberInput: FC = ({ if (!current) { return } - const currentInputValue = parseUnits(current.value.replace(/,/g, '') || '0', decimals) + // The input may contain an intermediate/unparseable string while the user is + // typing; guard against a parseUnits throw so an external value update never + // crashes the effect. + let currentInputValue: bigint + try { + currentInputValue = parseUnits(current.value.replace(/,/g, '') || '0', decimals) + } catch { + currentInputValue = BigInt(-1) // sentinel: force the DOM value to be overwritten + } if (currentInputValue !== value) { current.value = formatUnits(value, decimals) @@ -91,6 +101,7 @@ export const BigNumberInput: FC = ({ const { value } = typeof event === 'string' ? { value: event } : event.currentTarget if (value === '') { + setHasError(false) onChange(BigInt(0)) return } @@ -125,12 +136,16 @@ export const BigNumberInput: FC = ({ }] and value is: ${value}` console.warn(message) onError?.({ value, message }) + setHasError(true) + } else { + setHasError(false) } onChange(newValue) } const inputProps = { + 'aria-invalid': (hasError || undefined) as true | undefined, disabled, onChange: updateValue, placeholder, diff --git a/src/components/sharedComponents/Hash.tsx b/src/components/sharedComponents/Hash.tsx index 5022ee1f..f44594df 100644 --- a/src/components/sharedComponents/Hash.tsx +++ b/src/components/sharedComponents/Hash.tsx @@ -61,7 +61,12 @@ const Hash: FC = ({ aria-label="Copy" /> )} - {explorerURL && } + {explorerURL && ( + + )} ) } diff --git a/src/components/sharedComponents/TokenInput/index.tsx b/src/components/sharedComponents/TokenInput/index.tsx index 60d60626..d558d39b 100644 --- a/src/components/sharedComponents/TokenInput/index.tsx +++ b/src/components/sharedComponents/TokenInput/index.tsx @@ -202,7 +202,10 @@ const TokenInput: FC = ({ showBalance={showBalance} showTopTokens={showTopTokens} > - setIsOpen(false)} /> + setIsOpen(false)} + /> diff --git a/src/components/sharedComponents/ui/CopyButton/index.tsx b/src/components/sharedComponents/ui/CopyButton/index.tsx index 93a2b523..bff35858 100644 --- a/src/components/sharedComponents/ui/CopyButton/index.tsx +++ b/src/components/sharedComponents/ui/CopyButton/index.tsx @@ -77,10 +77,15 @@ export const CopyButton: FC = ({ height="fit-content" justifyContent="center" lineHeight="1" - outline="none" padding="0" + _focusVisible={{ + outline: '2px solid', + outlineColor: 'primary.default', + outlineOffset: '2px', + borderRadius: '2px', + }} textDecoration="none" - transition="background-color {durations.moderate}, border-color {durations.moderate}, color {durations.moderate" + transition="background-color {durations.moderate}, border-color {durations.moderate}, color {durations.moderate}" userSelect="none" whiteSpace="nowrap" width="fit-content" diff --git a/src/components/sharedComponents/ui/ExternalLink/index.tsx b/src/components/sharedComponents/ui/ExternalLink/index.tsx index feb27ac5..4af70c36 100644 --- a/src/components/sharedComponents/ui/ExternalLink/index.tsx +++ b/src/components/sharedComponents/ui/ExternalLink/index.tsx @@ -41,6 +41,7 @@ const LinkSVG: FC> = ({ ...restProps }) => ( export const ExternalLinkButton: FC = ({ children = , css, + rel = 'noopener noreferrer', target = '_blank', ...restProps }: LinkProps) => { @@ -57,9 +58,14 @@ export const ExternalLinkButton: FC = ({ fontWeight="400" height="fit-content" justifyContent="center" - outline="none" padding="0" - transition="background-color {durations.moderate}, border-color {durations.moderate}, color {durations.moderate" + _focusVisible={{ + outline: '2px solid', + outlineColor: 'primary.default', + outlineOffset: '2px', + borderRadius: '2px', + }} + transition="background-color {durations.moderate}, border-color {durations.moderate}, color {durations.moderate}" whiteSpace="nowrap" width="fit-content" _hover={{ @@ -73,6 +79,7 @@ export const ExternalLinkButton: FC = ({ cursor: 'not-allowed', opacity: 0.6, }} + rel={rel} target={target} {...restProps} > diff --git a/src/components/sharedComponents/ui/Modal/index.tsx b/src/components/sharedComponents/ui/Modal/index.tsx index 57b263d7..d72455e6 100644 --- a/src/components/sharedComponents/ui/Modal/index.tsx +++ b/src/components/sharedComponents/ui/Modal/index.tsx @@ -84,7 +84,12 @@ export const Modal: FC = ({ css, children, title, onClose, text, ...restP > {title} - {onClose && onClose()} />} + {onClose && ( + onClose()} + /> + )} {children ? children : 'No contents'} + + Skip to main content +
diff --git a/vite.config.ts b/vite.config.ts index e050c981..3244648d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -26,6 +26,7 @@ export default defineConfig({ }, test: { environment: 'jsdom', + exclude: ['**/node_modules/**', '.worktrees/**'], globals: true, setupFiles: ['./setupTests.ts'], },