Skip to content

style(address-book): DS migration demo — saved-addresses view (stacked on #2837) - #2856

Closed
kushagrasarathe wants to merge 307 commits into
feat/crypto-address-bookfrom
demo/ds-migration-2837
Closed

style(address-book): DS migration demo — saved-addresses view (stacked on #2837)#2856
kushagrasarathe wants to merge 307 commits into
feat/crypto-address-bookfrom
demo/ds-migration-2837

Conversation

@kushagrasarathe

@kushagrasarathe kushagrasarathe commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Demo PR for the design-system walkthrough recording: migrates the saved-addresses view from #2837 (Alek's crypto address book) to the design system. Styling and components only — no behavior, props, or logic changed.

Stacked branch: Alek's feat/crypto-address-book head + merge of feat/design-system (the DS primitives and semantic tokens do not exist on dev yet), + one migration commit (0937f67a3). Not for merge into Alek's branch as-is — it exists to show the migration shape; the real migration lands after #2813 merges to dev.

What the migration commit does:

  • SavedAddressesList: rows compose ListItem (deleted ActionListCard is gone on the DS line); getCardPosition replaces the hand-rolled position ternary; the chain mini-bubble and edit affordance take semantic tokens; the edit button keeps a 44px hit area via pseudo-element (touch law).
  • LastUsedPill: stale tone takes bg-background-badge-helper — the exact token the component's own comment was waiting for.
  • SavedAddressEditDrawer: deleted ErrorAlertNotification priority="error" (flow-level failure per design.md); legacy type/color classes → text-body-* / text-label-l / foreground-* tokens; off-scale px-5px-4; shadowSize="4" dropped (DS purple carries it by default).
  • SaveAddressPrompt: text-xs text-grey-1text-body-xs text-foreground-secondary (2 sites).
  • withdraw/crypto/page.tsx: success-message nickname renders as a bare string — PaymentSuccessView already styles the message line, so the wrapper span and its classes were redundant.

Merge-conflict resolutions (Alek × DS branch) keep his behavior with DS styling: the saved-addresses section renders as a Section, the nickname spans take semantic tokens.

Task

Demo PR — walkthrough recording, no tracking task on purpose.

Risks / breaking changes

None to prod: base is feat/crypto-address-book, and this PR is a demo artifact. Behavior parity is asserted by Alek's own tests passing unchanged (6/6 address-book, 16/16 withdraw-area, 3829 full suite).

QA

  • npx tsc --noEmit clean · full jest suite green · prettier clean.
  • node scripts/ds-lint-counts.mjs --check — ratchet fully green, 11/11 metrics at or below baseline (the address-book code had introduced legacyColorClasses 0→2, stockTextSize +2, classNameSitesInPages +1; all back to baseline).

Screenshots

Captured at 375×667 via the new withdraw-address-book fixture (/withdraw?__fixture=withdraw-address-book — zero backend). Assets live on pr-assets-2856, deleted post-merge.

Saved-addresses list (all three tones) Edit drawer
list drawer

Also adds the withdraw-address-book fixture entry (dev-tooling only): one saved address per last-used tone, relative dates so the tones never drift. The ds-shots job has no baseline for this new screen, so the fixture + these captures stand in as the visual evidence.

🤖 Generated with Claude Code

kushagrasarathe and others added 30 commits August 18, 2026 14:51
…ose X on balance warning, fix QR url fallback precedence
ds(12): /dev/ds foundations generated from the @theme token source
…rimitives

# Conflicts:
#	src/app/(mobile-ui)/dev/components/page.tsx
ds-06: primitives restyle — button, card, list item, navigation to the figma boards
ds-06: fold PeanutLoading into Loading — one loader
ds-06: input, toggle, badge, avatar to the figma boards
ds-06: ActionModal board restyle + migrate 10 modal stragglers
ds-06: card family consolidation — ActionListCard→ListItem, InfoCard tokens, MantecaDetailsCard deleted
…rimitives

# Conflicts:
#	src/components/Claim/Link/views/MantecaReviewStep.tsx
ds-06: re-land cards consolidation (#2720 merged into its stale stacked base)
…:75689

why: KR3 architecture move (thin page + flow hook + dumb views, matching
features/payments/flows/semantic-request) and the DS 07 home layout: bare
avatar top-left, rewards link top-right, centered balance with
add/send/request submenu, cta card slot composed as-is, activity feed on
the ListItem primitive.

- home/page.tsx is now a shim; all logic in src/features/home/
- balance visibility moved to a useSyncExternalStore over the persisted
  user preference so page/flow hook stay useState-free
- modal priority chain extracted unchanged into HomeModals
- TransactionCard, KycStatusItem, BadgeStatusItem, CardUnlockHistoryItem
  now render through the ListItem primitive (shared with /history)
- ListItem + Global/Card gain aria-label passthrough (replaces the
  CardUnlockHistoryItem button wrapper)
- withdraw quick action dropped from home per the figma board (3 submenu
  buttons) — flagged in the PR as a product-affecting change
why: AvatarWithBadge with no name renders a colorless, invisible circle —
seen on harness users seeded without a username.
why: new shared-state logic (toggle + persist + multi-subscriber sync)
needs a failing check if it breaks.
- NaN-guard the balance-warning env thresholds
- drop the user non-null assertion in BalanceWarningModal close
- mount-gate WelcomeUnlockModal + IosPwaInstallModal chunks
- accessible names for the profile link and balance toggle (new home.*
  keys in en/es-419/pt-BR)
- balance-visibility store: in-memory session cache so the toggle works
  when localStorage writes fail; map lookup per render instead of a
  localStorage read; posthog fires after the write
- eye toggle stays reachable when the balance query errors (old-page
  parity; it also controls activity amounts)
- HistorySkeleton padding matches ListItem rows (no load jump)
- useHomeFlow test (page gate, mount effects, avatarName derivation)
why: the figma home board renders initials on the avatar palette, never a
generic icon. usernameless users now fall back to full-name initials, and
a user with no name at all gets an avatar-yellow token circle (the
palette's no-name default) instead of a plain person icon.
One layout shell for (mobile-ui), (setup) and the public payment wrapper:
AppShell variant app|onboarding with nav/banner/modals slots. Desktop now
shows the same centered mobile column (max-w-md) — WalletNavigation sidebar
and TopNavbar are gone. Bottom nav rebuilt to the figma navigation board
17802:61534: home/card/support pill tabs (active = white pill, 68x52px
pressables) + the 52px pink QR circle. Support unread badge preserved.
…style

Build the four missing DS components from the figma boards:

- Notification (0_Bruddle, board 17802:61535): 5 priorities on the
  background-badge tokens, body / title+body, dismiss, up to 2 CTAs
  reusing Button size=small. No prod consumer yet — /dev/ds is the
  consumer for now.
- LinkButton (0_Bruddle, board 17980:17351): Body/XS underlined
  standalone navigation link, foreground-secondary at rest, black on
  hover, optional trailing icon, 44px hit area.
- Accordion (0_Bruddle, board 17802:61540): styled compound wrapper
  over the radix base; BridgeLimitsView migrated off its inline radix
  styling.
- Slider (Global, board 17802:61531): restyle to tokens — track
  bg-border-disabled, fill/tick action-primary, bordered thumb with
  shadow-2 and 44px hit area, label-l value text. Snap logic untouched.

Each component gets a /dev/ds page with all variants; nav additions are
additive-only to limit conflicts with the ds/07 branch.
TransactionDetailsReceipt (1009 LoC) split into an orchestrator plus
ReceiptDetailsCard / ReceiptActions / ReceiptTokenRows / ReceiptReferralNudge
views, a ReceiptRow list-row primitive and two hooks (useTokenDisplay via
tanstack query, useReceiptActions for every charges/requests/claim-link call
so views import no api modules). The details card owns layout: px-4 +
divide-y dashed dividers, so rows and provider sub-rows carry no last-row
border logic — shouldHideBorder/shouldHideGroupBorder removed from the view
model. Provider rows (card payment, Manteca, Bridge deposit) and the perk
receipt moved off PaymentInfoRow onto ReceiptRow; legacy palette classes
replaced with semantic tokens. i18n keys unchanged.
…ring, controlled first-paint, cta tuple type

- tailwind-merge misclassifies text-body-s/xs as text-color and strips them
  against text-foreground-* in the same twMerge call; keep the size token
  outside the merge (Accordion trigger/content, LinkButton)
- slider thumb ring: outline-none poisons --tw-outline-style, add
  focus-visible:outline-solid so the ring paints
- seed slider internal state from the controlled value so a controlled
  slider does not first paint at 100% and jump
- ctas typed as a 1-2 tuple so a third CTA is a compile error, not a
  silent drop; key by index (labels can repeat)
…450)

The advisory eslint job reached 0 errors after the sprint-155 cleanup, so
flip it to blocking: drop continue-on-error and add it to ci-success.needs.
Also remove the 6 eslint-disable directives the cleanup left unused, so
the branch lints with zero errors and zero unused-directive warnings.
Review finding: when eslint is the failing job the diagnostic echo showed
only the other five jobs, all green, hiding the actual cause.
ci: make eslint blocking now errors are at zero (TASK-21450)
Wire scripts/ds-lint-counts.mjs --check into CI as a blocking job: the five
DS debt counts may only go down. Allowlist the /dev/ds and /dev/components
showcase pages for every metric (they render token values programmatically
by design — they were the whole inlineStyle 207->211 false regression) and
tighten the baseline to current-tree truth, locking in the DS 05/06 gains
(stock text sizes 1392->1103, non-DS view classes 473->439).
kushagrasarathe and others added 21 commits August 27, 2026 15:57
…opped

The background-page fade washed content out too aggressively in real use;
the .35-alpha soft shadow reads better. Gradient wrapper reverted to plain.
…n every non-failed state

Verified every getTitle output against board 17490:115877. The board's
head model is: the TITLE carries the transaction-type wording, the
BADGE carries the state. Its pending card-payment drawer
(Activity/CardPayment, 17835:81079) is titled 'Paid to Museumsinsel'
with a Pending badge — not 'Paying to'.

Board copy vs code:
- completed QR/card pay: 'Paid to {name}' — already matched
- pending QR/card pay: board 'Paid to {name}'; code said
  'Paying to {name}' -> now 'Paid to {name}' (cancelled follows the
  same rule: type wording + Cancelled badge + struck amount)
- failed card pay: 'Payment to {name}' — already matched (2790915)
- request pot: 'Request', black amount, no badge — already matched
  (2790915)

'title.payingTo' lost its last consumer — removed from en / es-419 /
pt-BR (es-AR never carried it).

Board-uncovered cases (kept as-is, board shows no send / receive /
add / withdraw / bank / claim receipts): 'Sent to' / 'Sending to',
'Received from', 'Added from' / 'Adding from', 'Withdrew to' /
'Withdrawing to', 'Claimed to' / 'Claiming to', link-tx labels, and
the self-contained 'Failed QR payment attempt' label.
Kush + Hugo: the doc site was styling its own scaffolding on raw tokens
while documenting the primitives that already own those shapes. The
chrome now composes the DS components wherever one fits:

- DesignNote -> Notification (was a hand-rolled copy of the banner)
- CatalogCard -> ListItem + IconBubble + chevron inside the Link
- DocHeader -> TitleBlock size=m (h1 kept for heading semantics)
- SectionDivider -> Divider
- DoDont -> Card wells + IconBubble verdict dots
- WhenToUse -> Card wells
- Playground -> Card panels; controls run on BaseSelect / BaseInput /
  Checkbox (the primitives the playground documents)
- UsageAudit -> Card hero/stat-tiles/item-rows/footnote; BaseInput search
- ds root page -> Card hero + TitleBlock, Card stat tiles, ListItem
  section index, Card quick-rules
- StatusTag already wrapped StatusBadge; TierNav already borrows the
  SegmentedControl vocabulary (verified, unchanged)

Legitimately still custom (no DS primitive fits): PropsTable (no DS
table), CodeBlock (no DS code surface; copy affordance is a bare icon
button), DocSidebar (compact 192px nav rail — ListItem's 48px card rows
triple its height), DocSection (two-column doc layout; TitleBlock lacks
the heading-xs step and can't host the inline code toggle), UsageAudit
filter/status chips (7-tone semantic palette, no DS chip primitive),
DocPage (plain stack wrapper).

Showcase content untouched; nav drift test green.
…etch

useCurrency seeded `code` from the prop on the first render and then ignored
the prop for the rest of its life. Both callers derive the currency from
useSearchParams(), which is empty on the first render of a statically exported
page — so the withdraw page passed null, the hook latched it, never fetched, and
price stayed null forever behind an unresolvable loader (#1848).

- useCurrency: sync `code` when the prop changes, report the sync gap as
  isLoading, add refetch(), ignore superseded in-flight responses, and drop a
  stale rate when the next fetch fails.
- New shared RateUnavailable (alert + retry) wired into all four rate-dependent
  flows: InputAmountStep, withdraw/manteca (which had no error branch at all),
  MantecaReviewStep (rendered '1 USD = undefined ARS'), and the bridge bank
  flow.
- i18n: move rateUnavailable from addMoney.errors to the shared errors namespace
  across all four catalogs rather than duplicating the string.

Fixes #1848.

Replaces #2707, which could not be merged: resolving its conflict with dev
required a merge commit, and that drags in 7 pre-2026-08-18 unsigned
src/content bumps the signature ruleset rejects.
…lable key

The marketing subsets are generated from the app catalogs and mirror the
`errors` namespace, so moving rateUnavailable into it left them stale — which
renders as a raw message key on the marketing site.
Review follow-ups — each one re-opened the dead end this PR set out to close:

- withdraw/manteca: a retry put the page back into the bare loader with no
  header, so a stalling retry froze the screen again. Both rate states now keep
  NavHeader mounted.
- InputAmountStep: the retry was hidden whenever another error or a blocking
  limits card was showing, even though the rate block is what disables Continue
  — leaving no way to clear it. Now always rendered when the rate failed.
- add-money bank: Continue is disabled while the rate is in flight but showed no
  loading state, reading as an unexplained dead button.
The three audit data files (~524KB / 9,041 lines) shipped in every prod
client bundle, guarded only by a runtime notFound(). Each page now loads
its data through an inline statically-foldable condition (the
DEV_TOOLS_ENABLED expression) around a require(), so webpack drops the
dead branch in prod. Both next configs now define NEXT_PUBLIC_VERCEL_ENV
unconditionally — next skips null env values, so without the '' fallback
the preview leg never folded off-Vercel and the data stayed bundled.

Verified: prod-build client chunks 311KB/65KB/49KB -> 10.6KB/1.9KB/1.3KB;
grep of .next for data-only literals returns nothing; dev still renders
all three pages with full data; preview keeps them (VERCEL_ENV=preview
folds the gate to true).
Jota (PR #2813): 'too much padding on top' on the badge receipt. The
drawer stacked py-4 on DrawerContent plus p-4 on the body above the
handle's own spacing, while the TX-details chrome it mirrors (board
17835:84492: handle 8px above / 24px below, then content) starts content
right after the handle — like TransactionDetailsDrawer already does.
Now pb-4 + px-4 only.
…etry-dev

feat(native): make the deferred-link match rate measurable
ci: resolve release versions as <major>.<build>.<ota> instead of typing them
…lance

fix: block account deletion while the wallet still holds funds
…ze-dev

fix(fx): stop the add-money and withdraw screens freezing on a rate fetch
…-level

perf(capgo): stop logging transient update failures at error level
…loop-dev

fix(kyc): launch Manteca RFIs via start-action instead of the generic resubmit
…op, native routing)

Conflicts (5 files), doctrine DS-structure-wins / dev-behavior-ported:
- add-money bank page, withdraw/manteca, InputAmountStep, MantecaReviewStep:
  dev's #2843 rate-failure recovery (RateUnavailable with always-reachable
  retry) ported; its ErrorAlert internals swapped to the DS Notification
  (ErrorAlert is retired on this branch) and its PeanutLoading calls to the
  one DS Loading. The branch's older inline rate Notification (whose i18n key
  dev moved) removed as superseded.
- AddressLink: DS tokens + tw wrapper kept; dev's capacitor target behavior
  ported.

Post-merge fixes so nothing from this PR ships rule breaks (ruling 3):
- manteca page's repeated header+gate block extracted to
  Global/RateUnavailable/RateGateScreen (new component, justified in its
  docblock: the inline repeat tripped the page de-inlining ratchet)
- useCurrency docstring reworded — 'useSearchParams()' in a comment was a
  ratchet false positive
- add-money rate-failure test updated to the RateUnavailable contract
  (same copy + retry button instead of the retired testid)

Gates: prettier clean, typecheck clean, 313/313 suites (3801 tests),
ds-lint ratchet no metric increased.
qa: fixtures, screenshot diffing, and a CI job that can actually fail
fix: three production bugs the fixture work surfaced
…ribing labels render bare

Reported case: a completed crypto deposit's receipt showed only
'kushagra.peanut.me' with no type wording. getTitle DID build
'Added from …', but VerifiedUserLabel's AddressLink lane fires on any
crypto-address username and discards the worded name prop entirely,
rendering the ENS-resolved address instead. Same drop hit every family
whose userName is a raw 0x (crypto add/withdraw, P2P with address
peers, link claims from addresses, claim-external).

- VerifiedUserLabel: AddressLink lane now only wins when the caller
  passed no worded copy (name === username). List rows unchanged —
  their name is already the resolved/shortened handle.
- TransactionDetailsHeaderCard: reverse-resolve address usernames via
  usePrimaryNameServer (the TransactionCard pattern) before wording,
  so the title reads 'Added from {ens}', falling back to the
  shortened 0x.
- SELF_DESCRIBING_NAME_KEYS: reaper fail copy, refund labels, the
  failed-QR label and the open-request label early-return bare from
  getTitle — kills 'Sending to Send didn't complete' and
  'Received from Refund from X' compounds (folds in the old ad-hoc
  failedQrPayment check).
- Own open request pots title as 'You requested' (new
  transaction.title.youRequested in en/es-419/pt-BR, translations
  aligned with the existing peanutActionDetailsCard key); unresolved
  incoming requests stay bare 'Request'.
- Tests: VerifiedUserLabel is no longer mocked in the header suite
  (that mock is what hid the drop); new cases lock ENS deposit,
  no-ENS fallback, reaper, refund, and both request wordings.
…-migration-2837

# Conflicts:
#	src/app/(mobile-ui)/withdraw/crypto/page.tsx
#	src/components/AddWithdraw/AddWithdrawRouterView.tsx
#	src/components/Common/SavedAccountsView.tsx
#	src/components/Withdraw/views/Confirm.withdraw.view.tsx
ActionListCard and ErrorAlert no longer exist on the DS line, and the
address-book rows predate the semantic tokens. Rows now compose ListItem
(getCardPosition replaces the hand-rolled position ternary), the stale
pill takes the badge-helper token its own comment was waiting for, the
edit drawer reports failures through Notification, and the success
message renders bare because PaymentSuccessView already styles that
line. No behavior, props, or logic changed — ds-lint ratchet is clean.
@vercel

vercel Bot commented Aug 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
peanut-wallet Ready Ready Preview Aug 27, 2026 4:04pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

@kushagrasarathe

Copy link
Copy Markdown
Contributor Author

/chip review

@chip-peanut-bot chip-peanut-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chip review: Review clean

The saved-address design-system migration preserves selection, editing, failure feedback, and success-label behavior; no actionable correctness, security, adversarial, or slop finding survived verification.

Checked clean

  • Pinned HEAD, exact base, merge base, trusted author, PR title, and PR description matched the supplied review target.
  • Reviewed the five-file migration commit and the address-book-related merge resolutions layered over the design-system parent.
  • Verified saved-address row grouping, chain-name fallback, row selection, edit-event propagation, and the 44px edit hit target against ListItem and Card contracts.
  • Verified rename/delete busy states and failure feedback retain the drawer's existing behavior through Notification.
  • Verified saved nicknames and addresses remain React text/content rather than unsafe HTML, with no new secret, authorization, or trust-boundary exposure.
  • Verified the success nickname renders under PaymentSuccessView's existing message typography and prefix behavior.
  • git diff --check passed and the design-system lint ratchet passed all 11 metrics.
  • Targeted Jest suites could not start because the detached worktree has no local dependencies and the shared installation could not resolve next-intl/ts-jest for this project root.

Second opinion skipped: openrouter-empty-reply.

Exact head: 0937f67a38ae · Context: repo

…last-used tone

Lets the migrated saved-addresses view render with zero backend
(?__fixture=withdraw-address-book) and gives ds-shots a state to
diff once a baseline exists. Dates are relative so the tones do
not drift.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants