{
expect(onChangeValue).toBe('0.001')
})
+ it('strips leading zeros from the whole-number part (#370)', () => {
+ let onChangeValue = ''
+ const onChange = (value: string) => {
+ onChangeValue = value
+ }
+ const { container } = render( )
+ const input = container.querySelector('input') as HTMLInputElement
+
+ fireEvent.change(input, { target: { value: '007' } })
+ expect(onChangeValue).toBe('7')
+
+ fireEvent.change(input, { target: { value: '00.5' } })
+ expect(onChangeValue).toBe('0.5')
+
+ fireEvent.change(input, { target: { value: '0' } })
+ expect(onChangeValue).toBe('0')
+
+ fireEvent.change(input, { target: { value: '0.001' } })
+ expect(onChangeValue).toBe('0.001')
+
+ fireEvent.change(input, { target: { value: '010.25' } })
+ expect(onChangeValue).toBe('10.25')
+ })
+
it('rejects multiple decimal points', () => {
let result = ''
const onChange = (value: string) => {
diff --git a/src/components/AmountInput.tsx b/src/components/AmountInput.tsx
index b2d44bb4..329be0ff 100644
--- a/src/components/AmountInput.tsx
+++ b/src/components/AmountInput.tsx
@@ -109,9 +109,9 @@ export function AmountInput({
value={value}
onFocus={(e) => e.target.select()}
onChange={(e) => {
- const v = sanitizeAmount(e.target.value);
+ const v = sanitizeAmount(e.target.value)
// If editing existing value, typing should replace not append when field was pre-filled
- onChange?.(v);
+ onChange?.(v)
}}
style={{
flex: 1,
@@ -138,7 +138,14 @@ export function AmountInput({
+
Min 1 USDC — Max {cap ?? '—'} USDC
+
{isRateStale ? `Rate updated ${rateAgeSeconds}s ago — may be outdated. Refresh before confirming.` : `Live rate — updated ${rateAgeSeconds}s ago at ${priceFetchedAt.toLocaleTimeString()}`} @@ -201,14 +261,37 @@ export function Deposit({ onDone }: DepositProps) { type="button" onClick={() => refreshVault()} aria-label="Refresh exchange rate" - style={{ fontFamily: 'var(--font-body)', fontSize: 'var(--type-caption)', fontWeight: 600, color: 'var(--ink)', background: 'var(--ink-06)', border: '1px solid var(--ink-12)', borderRadius: 'var(--radius-pill)', padding: '4px 10px', cursor: 'pointer' }} + style={{ + fontFamily: 'var(--font-body)', + fontSize: 'var(--type-caption)', + fontWeight: 600, + color: 'var(--ink)', + background: 'var(--ink-06)', + border: '1px solid var(--ink-12)', + borderRadius: 'var(--radius-pill)', + padding: '4px 10px', + cursor: 'pointer', + }} > Refresh rate
{t.rich('successBody', { - shares: (n / price).toFixed(4), + shares: formatDecimal(n / price, 4), num, b: strong, count: HB_DATA.pool.projectsFunded, diff --git a/src/screens/Landing.tsx b/src/screens/Landing.tsx index d487733b..ac4eedfb 100644 --- a/src/screens/Landing.tsx +++ b/src/screens/Landing.tsx @@ -4,7 +4,6 @@ import { useTranslations } from 'next-intl' import { Button, StatBlock } from '../components' import { LiveHelio } from '../brand/LiveHelio' import { HB_DATA } from '../data' -import type { Screen } from '../types' /** * Landing — public hero. The live Helio dominates; three counters deep-link to @@ -12,10 +11,10 @@ import type { Screen } from '../types' */ export interface LandingProps { onConnect: () => void - onNav: (screen: Screen) => void + onExplore: () => void } -export function Landing({ onConnect, onNav }: LandingProps) { +export function Landing({ onConnect, onExplore }: LandingProps) { const t = useTranslations('Landing') const d = HB_DATA const steps = [1, 2, 3, 4] as const @@ -62,7 +61,7 @@ export function Landing({ onConnect, onNav }: LandingProps) { -