Skip to content

feat(frontend): tx history drawer, pending overlay, card skeleton, inline field error#960

Merged
CMI-James merged 1 commit into
theblockcade:mainfrom
Xueen-code:feat/frontend-bundle-926-927-929-931
Jun 25, 2026
Merged

feat(frontend): tx history drawer, pending overlay, card skeleton, inline field error#960
CMI-James merged 1 commit into
theblockcade:mainfrom
Xueen-code:feat/frontend-bundle-926-927-929-931

Conversation

@Xueen-code

@Xueen-code Xueen-code commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Summary

All four components are exported from frontend/src/components/v1/index.ts.

Closes

closes #926
closes #927
closes #929
closes #931

Test plan

  • WalletTxHistoryDrawer — closed/open states, transaction list, empty state, type/amount rendering
  • TxPendingOverlay — hidden when visible=false, shows message/hash/cancel, spinner present
  • DashboardCardSkeleton — correct card count, variant CSS classes, aria-busy
  • InlineFieldError / useFieldValidation — null hides element, error renders with role="alert", blur triggers validation, aria-invalid reflects state

Run: cd frontend && npx vitest run tests/unit/WalletTxHistoryDrawer.test.tsx tests/unit/TxPendingOverlay.test.tsx tests/unit/DashboardCardSkeleton.test.tsx tests/unit/InlineFieldError.test.tsx

31 tests, all passing.

Summary by CodeRabbit

  • New Features
    • Added a Wallet Transaction History drawer with transaction icons, amounts, timestamps, optional hash tooltips, and status badges.
    • Added a transaction-pending overlay with a spinner, customizable message, optional truncated tx hash, and delayed cancel action.
    • Added dashboard card skeleton loading variants for metric, list, and chart layouts.
    • Added inline field error messaging plus a validation hook for touched/blur-based errors.
  • Bug Fixes
    • Enhanced accessibility with ARIA live/status/alert semantics for loading and error states.
  • Tests
    • Added unit tests covering the drawer, overlay, skeleton rendering, and inline error/validation behavior.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c148d46-3a95-4b9a-a7a1-bddd99a16a16

📥 Commits

Reviewing files that changed from the base of the PR and between fa1885e and 43ba3e4.

📒 Files selected for processing (13)
  • frontend/src/components/v1/DashboardCardSkeleton.css
  • frontend/src/components/v1/DashboardCardSkeleton.tsx
  • frontend/src/components/v1/InlineFieldError.css
  • frontend/src/components/v1/InlineFieldError.tsx
  • frontend/src/components/v1/TxPendingOverlay.css
  • frontend/src/components/v1/TxPendingOverlay.tsx
  • frontend/src/components/v1/WalletTxHistoryDrawer.css
  • frontend/src/components/v1/WalletTxHistoryDrawer.tsx
  • frontend/src/components/v1/index.ts
  • frontend/tests/unit/DashboardCardSkeleton.test.tsx
  • frontend/tests/unit/InlineFieldError.test.tsx
  • frontend/tests/unit/TxPendingOverlay.test.tsx
  • frontend/tests/unit/WalletTxHistoryDrawer.test.tsx
✅ Files skipped from review due to trivial changes (3)
  • frontend/src/components/v1/InlineFieldError.css
  • frontend/src/components/v1/TxPendingOverlay.css
  • frontend/src/components/v1/WalletTxHistoryDrawer.css
🚧 Files skipped from review as they are similar to previous changes (10)
  • frontend/tests/unit/WalletTxHistoryDrawer.test.tsx
  • frontend/src/components/v1/TxPendingOverlay.tsx
  • frontend/tests/unit/InlineFieldError.test.tsx
  • frontend/tests/unit/TxPendingOverlay.test.tsx
  • frontend/src/components/v1/DashboardCardSkeleton.tsx
  • frontend/src/components/v1/index.ts
  • frontend/src/components/v1/WalletTxHistoryDrawer.tsx
  • frontend/tests/unit/DashboardCardSkeleton.test.tsx
  • frontend/src/components/v1/DashboardCardSkeleton.css
  • frontend/src/components/v1/InlineFieldError.tsx

📝 Walkthrough

Walkthrough

Added four shared frontend primitives: a dashboard card skeleton, inline field error/validation hook, transaction pending overlay, and wallet transaction history drawer. The v1 barrel re-exports the new APIs, and unit tests cover rendering, accessibility attributes, and empty or fallback states.

Changes

Shared v1 frontend primitives

Layer / File(s) Summary
Dashboard skeleton
frontend/src/components/v1/DashboardCardSkeleton.tsx, frontend/src/components/v1/DashboardCardSkeleton.css, frontend/tests/unit/DashboardCardSkeleton.test.tsx
DashboardCardSkeleton renders metric, list, and chart grids with count clamping, variant-specific classes, loading ARIA, and matching unit coverage.
Inline error and validation hook
frontend/src/components/v1/InlineFieldError.tsx, frontend/src/components/v1/InlineFieldError.css, frontend/tests/unit/InlineFieldError.test.tsx
InlineFieldError renders alert text when an error exists, and useFieldValidation tracks touched state, blur/change validation, and ARIA wiring; tests cover absent/error states and input behavior.
Pending transaction overlay
frontend/src/components/v1/TxPendingOverlay.tsx, frontend/src/components/v1/TxPendingOverlay.css, frontend/tests/unit/TxPendingOverlay.test.tsx
TxPendingOverlay shows a status overlay with optional message, truncated hash, cancel action, and timer-based cancel disabling; tests cover visibility, hash rendering, cancel, and spinner presence.
Wallet transaction history drawer
frontend/src/components/v1/WalletTxHistoryDrawer.tsx, frontend/src/components/v1/WalletTxHistoryDrawer.css, frontend/tests/unit/WalletTxHistoryDrawer.test.tsx
WalletTxHistoryDrawer renders a drawer with transaction rows, localized timestamps, optional price and hash fields, status pills, and an empty state; tests cover open/empty states and close handling.
v1 public exports
frontend/src/components/v1/index.ts
The v1 barrel re-exports the new drawer, overlay, skeleton, and inline error components, plus their props, types, and useFieldValidation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I hopped through cards that shimmered bright,
And field errors glowed just right.
A pending tx spun in the dew,
While drawer trails lined up in view.
Hoppy APIs for bunny crew 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the four component additions in the PR.
Linked Issues check ✅ Passed The PR implements the requested frontend components and tests them, matching the linked issue scope [#926, #927, #929, #931].
Out of Scope Changes check ✅ Passed The diff stays within the requested frontend component additions and related tests, with no unrelated changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Stylelint (17.13.0)
frontend/src/components/v1/InlineFieldError.css

Error: ENOENT: no such file or directory, open '/.stylelintrc.json'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async #readConfiguration (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:83:26)
at async load (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:20:48)
at async Explorer.load (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:23:20)
at async getConfigForFile (file:///usr/local/lib/node_modules/stylelint/lib/getConfigForFile.mjs:72:5)
at async resolveOptionValue (file:///usr/local/lib/node_modules/stylelint/lib/utils/resolveOptionValue.mjs:27:24)
at async standalone (file:///usr/local/lib/node_modules/stylelint/lib/standalone.mjs:127:22)

frontend/src/components/v1/DashboardCardSkeleton.css

Error: ENOENT: no such file or directory, open '/.stylelintrc.json'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async #readConfiguration (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:83:26)
at async load (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:20:48)
at async Explorer.load (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:23:20)
at async getConfigForFile (file:///usr/local/lib/node_modules/stylelint/lib/getConfigForFile.mjs:72:5)
at async resolveOptionValue (file:///usr/local/lib/node_modules/stylelint/lib/utils/resolveOptionValue.mjs:27:24)
at async standalone (file:///usr/local/lib/node_modules/stylelint/lib/standalone.mjs:127:22)

frontend/src/components/v1/TxPendingOverlay.css

Error: ENOENT: no such file or directory, open '/.stylelintrc.json'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async #readConfiguration (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:83:26)
at async load (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:20:48)
at async Explorer.load (/usr/local/lib/node_modules/stylelint/node_modules/cosmiconfig/dist/Explorer.js:23:20)
at async getConfigForFile (file:///usr/local/lib/node_modules/stylelint/lib/getConfigForFile.mjs:72:5)
at async resolveOptionValue (file:///usr/local/lib/node_modules/stylelint/lib/utils/resolveOptionValue.mjs:27:24)
at async standalone (file:///usr/local/lib/node_modules/stylelint/lib/standalone.mjs:127:22)

  • 1 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
frontend/tests/unit/TxPendingOverlay.test.tsx (1)

70-83: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the timer-based cancel disabling path with fake timers.

Line 70+ checks initial enabled state, but there’s no assertion that cancel becomes disabled after 3 seconds or that click invokes onCancel before disable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/tests/unit/TxPendingOverlay.test.tsx` around lines 70 - 83, The
TxPendingOverlay test suite only verifies the cancel button starts enabled, but
it does not cover the timer-driven disable behavior or the pre-disable click
path. Update the TxPendingOverlay tests to use fake timers, assert that the
cancel button becomes disabled after 3 seconds, and verify that clicking the
cancel control calls onCancel before the timeout expires; use the existing
TxPendingOverlay render setup and tx-overlay-cancel test id to locate the
assertions.
frontend/tests/unit/InlineFieldError.test.tsx (1)

92-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an assertion for the aria-describedby linkage.

Line 92+ verifies aria-invalid, but it doesn’t verify that the input points to the rendered error element id when an error exists.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/tests/unit/InlineFieldError.test.tsx` around lines 92 - 104, The
InlineFieldError tests currently check only aria-invalid, so they miss verifying
the error-message association. Update the TestInput assertions to also check the
input’s aria-describedby when validation fails, and ensure it matches the
rendered error element id produced by the InlineFieldError path. Use the
existing test helpers and the field-input element to confirm the linkage, while
keeping the no-error case unchanged unless it should explicitly assert the
absence of the descriptor.
frontend/tests/unit/WalletTxHistoryDrawer.test.tsx (1)

83-99: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a malformed-timestamp test case.

Given timestamp fallback handling in the component, add one case that passes an invalid timestamp and asserts a stable fallback rendering.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/tests/unit/WalletTxHistoryDrawer.test.tsx` around lines 83 - 99, Add
a malformed-timestamp test to the WalletTxHistoryDrawer test suite so the
timestamp fallback path is covered. Extend the existing WalletTxHistoryDrawer
rendering tests by passing one transaction with an invalid timestamp value and
assert the drawer renders a stable fallback display instead of crashing or
showing an unstable value. Use the WalletTxHistoryDrawer component and the
existing transaction test setup/helpers to keep the new case consistent with the
current assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@frontend/src/components/v1/TxPendingOverlay.css`:
- Around line 41-45: The reduced-motion override in TxPendingOverlay.css still
leaves .tx-pending-overlay__spinner animating, so it does not fully respect
prefers-reduced-motion. Update the `@media` (prefers-reduced-motion: reduce) rule
for .tx-pending-overlay__spinner to disable the animation entirely rather than
just slowing it down, keeping the pending overlay static for motion-sensitive
users.

In `@frontend/src/components/v1/WalletTxHistoryDrawer.css`:
- Around line 120-124: The .wallet-tx-history-drawer__hash styles currently
force long transaction hashes to stay on one line, which can overflow the drawer
on small screens. Update this rule to allow wrapping or truncation with overflow
handling and ellipsis as needed, while keeping the hash readable and preserving
the responsive layout of WalletTxHistoryDrawer.

In `@frontend/src/components/v1/WalletTxHistoryDrawer.tsx`:
- Around line 36-41: The fallback in formatTimestamp is unreachable because new
Date(ts).toLocaleString() does not throw on invalid input, so malformed
timestamps still render as "Invalid Date". Update formatTimestamp in
WalletTxHistoryDrawer to explicitly detect invalid dates before formatting, and
return the original timestamp (or another fallback) when the Date is invalid
instead of relying on try/catch.

---

Nitpick comments:
In `@frontend/tests/unit/InlineFieldError.test.tsx`:
- Around line 92-104: The InlineFieldError tests currently check only
aria-invalid, so they miss verifying the error-message association. Update the
TestInput assertions to also check the input’s aria-describedby when validation
fails, and ensure it matches the rendered error element id produced by the
InlineFieldError path. Use the existing test helpers and the field-input element
to confirm the linkage, while keeping the no-error case unchanged unless it
should explicitly assert the absence of the descriptor.

In `@frontend/tests/unit/TxPendingOverlay.test.tsx`:
- Around line 70-83: The TxPendingOverlay test suite only verifies the cancel
button starts enabled, but it does not cover the timer-driven disable behavior
or the pre-disable click path. Update the TxPendingOverlay tests to use fake
timers, assert that the cancel button becomes disabled after 3 seconds, and
verify that clicking the cancel control calls onCancel before the timeout
expires; use the existing TxPendingOverlay render setup and tx-overlay-cancel
test id to locate the assertions.

In `@frontend/tests/unit/WalletTxHistoryDrawer.test.tsx`:
- Around line 83-99: Add a malformed-timestamp test to the WalletTxHistoryDrawer
test suite so the timestamp fallback path is covered. Extend the existing
WalletTxHistoryDrawer rendering tests by passing one transaction with an invalid
timestamp value and assert the drawer renders a stable fallback display instead
of crashing or showing an unstable value. Use the WalletTxHistoryDrawer
component and the existing transaction test setup/helpers to keep the new case
consistent with the current assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e41fc469-3b23-4d4e-9dea-be2354f82b1f

📥 Commits

Reviewing files that changed from the base of the PR and between 98ef271 and fa1885e.

📒 Files selected for processing (13)
  • frontend/src/components/v1/DashboardCardSkeleton.css
  • frontend/src/components/v1/DashboardCardSkeleton.tsx
  • frontend/src/components/v1/InlineFieldError.css
  • frontend/src/components/v1/InlineFieldError.tsx
  • frontend/src/components/v1/TxPendingOverlay.css
  • frontend/src/components/v1/TxPendingOverlay.tsx
  • frontend/src/components/v1/WalletTxHistoryDrawer.css
  • frontend/src/components/v1/WalletTxHistoryDrawer.tsx
  • frontend/src/components/v1/index.ts
  • frontend/tests/unit/DashboardCardSkeleton.test.tsx
  • frontend/tests/unit/InlineFieldError.test.tsx
  • frontend/tests/unit/TxPendingOverlay.test.tsx
  • frontend/tests/unit/WalletTxHistoryDrawer.test.tsx

Comment on lines +41 to +45
@media (prefers-reduced-motion: reduce) {
.tx-pending-overlay__spinner {
animation-duration: 3s;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Disable the spinner for reduced-motion users.

animation-duration: 3s still leaves the spinner animating forever, so this does not actually honor prefers-reduced-motion. Turn the animation off in that media query so the pending overlay stays visually static for motion-sensitive users.

Suggested fix
 `@media` (prefers-reduced-motion: reduce) {
   .tx-pending-overlay__spinner {
-    animation-duration: 3s;
+    animation: none;
   }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@media (prefers-reduced-motion: reduce) {
.tx-pending-overlay__spinner {
animation-duration: 3s;
}
}
`@media` (prefers-reduced-motion: reduce) {
.tx-pending-overlay__spinner {
animation: none;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/v1/TxPendingOverlay.css` around lines 41 - 45, The
reduced-motion override in TxPendingOverlay.css still leaves
.tx-pending-overlay__spinner animating, so it does not fully respect
prefers-reduced-motion. Update the `@media` (prefers-reduced-motion: reduce) rule
for .tx-pending-overlay__spinner to disable the animation entirely rather than
just slowing it down, keeping the pending overlay static for motion-sensitive
users.

Comment on lines +120 to +124
.wallet-tx-history-drawer__hash {
font-family: monospace;
font-size: 0.7rem;
white-space: nowrap;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Prevent long hashes from overflowing the drawer.

white-space: nowrap without wrap/ellipsis handling can push the drawer wider on small screens. Let the hash wrap or truncate so the responsive layout stays intact.

Suggested fix
 .wallet-tx-history-drawer__hash {
   font-family: monospace;
   font-size: 0.7rem;
-  white-space: nowrap;
+  overflow-wrap: anywhere;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.wallet-tx-history-drawer__hash {
font-family: monospace;
font-size: 0.7rem;
white-space: nowrap;
}
.wallet-tx-history-drawer__hash {
font-family: monospace;
font-size: 0.7rem;
overflow-wrap: anywhere;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/v1/WalletTxHistoryDrawer.css` around lines 120 - 124,
The .wallet-tx-history-drawer__hash styles currently force long transaction
hashes to stay on one line, which can overflow the drawer on small screens.
Update this rule to allow wrapping or truncation with overflow handling and
ellipsis as needed, while keeping the hash readable and preserving the
responsive layout of WalletTxHistoryDrawer.

Comment on lines +36 to +41
function formatTimestamp(ts: string): string {
try {
return new Date(ts).toLocaleString();
} catch {
return ts;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

formatTimestamp fallback is unreachable for invalid dates.

Line 38 won’t throw for malformed timestamps, so Line 40 never runs; users will see "Invalid Date" instead of a fallback value.

Proposed fix
 function formatTimestamp(ts: string): string {
-  try {
-    return new Date(ts).toLocaleString();
-  } catch {
-    return ts;
-  }
+  const date = new Date(ts);
+  if (Number.isNaN(date.getTime())) return ts;
+  return date.toLocaleString();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function formatTimestamp(ts: string): string {
try {
return new Date(ts).toLocaleString();
} catch {
return ts;
}
function formatTimestamp(ts: string): string {
const date = new Date(ts);
if (Number.isNaN(date.getTime())) return ts;
return date.toLocaleString();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/components/v1/WalletTxHistoryDrawer.tsx` around lines 36 - 41,
The fallback in formatTimestamp is unreachable because new
Date(ts).toLocaleString() does not throw on invalid input, so malformed
timestamps still render as "Invalid Date". Update formatTimestamp in
WalletTxHistoryDrawer to explicitly detect invalid dates before formatting, and
return the original timestamp (or another fallback) when the Date is invalid
instead of relying on try/catch.

@Xueen-code Xueen-code force-pushed the feat/frontend-bundle-926-927-929-931 branch from fa1885e to 43ba3e4 Compare June 25, 2026 18:51
@CMI-James CMI-James merged commit ced5f07 into theblockcade:main Jun 25, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants