Skip to content

feat(uiux): design dashboard widget empty and error states - #1617

Open
DeePrincipal-dev-lang wants to merge 2 commits into
Remitwise-Org:mainfrom
DeePrincipal-dev-lang:uiux/dashboard-empty-error-states
Open

feat(uiux): design dashboard widget empty and error states#1617
DeePrincipal-dev-lang wants to merge 2 commits into
Remitwise-Org:mainfrom
DeePrincipal-dev-lang:uiux/dashboard-empty-error-states

Conversation

@DeePrincipal-dev-lang

Copy link
Copy Markdown

Summary

What this does

Wires up and validates the empty and error states for all five dashboard widgets. The shared
WidgetEmptyState and WidgetErrorState primitives were already present in components/ui/; this
PR ensures they are correctly integrated, tested, and documented.

CTA destinations

┌────────────┬────────────┬───────────────────┬─────────────────────────────────────────────┐
│ Widget │ Empty-stat │ Destination route │ Rationale │
│ │ e CTA │ │ │
│ │ label │ │ │
├────────────┼────────────┼───────────────────┼─────────────────────────────────────────────┤
│ MoneyDistr │ "Set up │ /split │ Widget shows how funds are allocated; the │
│ ibutionWid │ your │ │ direct next action for a new user is to │
│ get │ split" │ │ configure the split │
├────────────┼────────────┼───────────────────┼─────────────────────────────────────────────┤
│ RecentTran │ "Send │ /send │ Transactions are created by sending money; │
│ sactionsWi │ money" │ │ most direct on-ramp to activity │
│ dget │ │ │ │
│ ibutionWid │ your │ │ next action for a new user is to configure the

│ get │ split" │ │ split

├────────────┼────────────┼───────────────────┼────────────────────────────────────────────────
─────┤
│ RecentTran │ "Send │ /send │ Transactions are created by sending money; most

│ sactionsWi │ money" │ │ direct on-ramp to activity

│ dget │ │ │

├────────────┼────────────┼───────────────────┼────────────────────────────────────────────────
─────┤
│ SavingsByG │ "Create a │ /goals │ Widget tracks named goals; no goals = no data,
so │
│ oalWidget │ goal" │ │ CTA goes to goal creation

├────────────┼────────────┼───────────────────┼────────────────────────────────────────────────
─────┤
│ GoalProgre │ "Add to │ /goals │ Per-goal widget; no progress = no
contributions, so │
│ ss │ this goal" │ │ CTA goes back to goals page

├────────────┼────────────┼───────────────────┼────────────────────────────────────────────────
─────┤
│ SixMonthTr │ (none) │ n/a │ Trends emerge from 6 months of activity across
all │
│ endsWidget │ │ │ categories; there is no single direct action to

│ │ │ │ "create trends". Copy sets expectations
instead: │
│ │ │ │ "Keep using Remitwise to see your financial

│ │ │ │ patterns over time."

└────────────┴────────────┴───────────────────┴────────────────────────────────────────────────
─────┘

Does this include loading/skeleton states?

No. Loading skeletons are explicitly deferred to UX-005 and untouched here.

Changes

  • MoneyDistributionWidget: percent strings converted to spoken form (57 percent) in aria-label
    and sr-only summary; aria-live="polite" added to sr-only summary

    ; fixed title casing in
    buildChartImageLabel call

  • SixMonthTrendsWidget: series names lowercased in aria-label (matching a11y test spec);
    sr-only summary replaced with a per-month breakdown (Jul: remittances $2,800, savings $1,200,
    …) for richer screen-reader output; removed unused buildChartSummary import
  • GoalProgress / RecentTransactionsWidget / SavingsByGoalWidget: CTA destination inline
    comments added; no logic changes required
  • GoalProgress.test.tsx (new): full test suite for default, error, empty, and state-precedence
    scenarios (17 tests, all passing)

Build / lint

  • npm run build: passes — all TS errors are pre-existing in unrelated files
    (ReceiptPageContent, FormattedNumber, PolicyDetail, AddressDisplay, Section)
  • npm run lint on changed files: 0 errors, 0 warnings
  • Pre-existing lint errors (app/swap, app/transactions, NavigationSidebar, ChipList.test,
    lib/config/errors.ts) are unrelated and pre-date this branch

Test results

All widget-related tests pass:

┌───────────────────────────────────────┬──────────┐
│ Test file │ Result │
├───────────────────────────────────────┼──────────┤
│ GoalProgress.test.tsx │ ✅ 17/17 │
├───────────────────────────────────────┼──────────┤
│ MoneyDistributionWidget.test.tsx │ ✅ 14/14 │
├───────────────────────────────────────┼──────────┤
│ MoneyDistributionWidget.a11y.test.tsx │ ✅ 7/7 │
├─────────────────────────────────────────────────┼──────────┤
│ RecentTransactionsWidget.test.tsx │ ✅ 22/22 │
├─────────────────────────────────────────────────┼──────────┤
│ SavingsByGoalWidget.test.tsx │ ✅ 22/22 │
├─────────────────────────────────────────────────┼──────────┤
│ SixMonthTrendsWidget.test.tsx │ ✅ 11/11 │
├─────────────────────────────────────────────────┼──────────┤
│ SixMonthTrendsWidget.a11y.test.tsx │ ✅ 6/6 │
├─────────────────────────────────────────────────┼──────────┤
│ dashboard-widgets-smoke.test.tsx (widget cases) │ ✅ 10/12 │
└─────────────────────────────────────────────────┴──────────┘

Two unrelated pre-existing failures remain in dashboard-widgets-smoke.test.tsx: QuickActions
(renders buttons instead of links) and RemittanceTrendChart (ReferenceError: activeDot is not
defined). Neither is in scope.

Accessibility

  • Error states: role="alert" aria-live="assertive" — screen readers interrupt and announce the
    failure immediately
  • Empty states: role="status" — non-interrupting, polite informational announcement
  • Retry button: aria-label="Retry loading data" — explicit, action-oriented accessible name
  • CTA links/buttons: all labels are specific and action-oriented ("Set up your split", "Create
    a goal", "Send money", "Add to this goal") — not generic "click here"
  • Chart containers: role="img" with a full aria-label summary; sr-only

    with
    aria-live="polite" for dynamic data

  • Progress bars: aria-valuenow/min/max and aria-label on all role="progressbar" elements
  • Focus order: no focus traps introduced; error/empty states replace widget content in-place so
    tab order is unchanged
  • Icons: aria-hidden="true" throughout
  • Keyboard: CTA links have focus-visible:outline styles for visible keyboard focus

Responsive layout

  • WidgetEmptyState / WidgetErrorState use py-10 flex flex-col items-center gap-4 text-center —
    they occupy the same vertical space as the populated widget, preventing dashboard grid reflow
  • CTA button/link has min-h-11 max-w-full break-words — stays within bounds at 375 px; no
    clipping or awkward wrapping

Ambiguities resolved

  • SixMonthTrendsWidget empty vs no-data: the widget operates on static chartData (seeded mock).
    The isEmpty prop is the caller's responsibility to set when real data is absent. The empty
    state copy does not blame the user and sets forward expectations.
  • GoalProgress zero-progress vs no-contributions: distinguished by the isEmpty prop
    (caller-supplied) vs current === 0 (valid in-progress state shown as 0 % progress bar). The two
    are not conflated.

Closes #1316

Implements empty and error states for all five dashboard widgets per issue
Remitwise-Org#1316. The shared WidgetEmptyState and WidgetErrorState components were
already in place; this commit wires them correctly, fixes test failures
found during QA, and adds CTA destination documentation.

Changes:
- MoneyDistributionWidget: convert percent strings to spoken form ('57
  percent') in aria-label and sr-only summary; add aria-live='polite' to
  sr-only summary; fix title casing in buildChartImageLabel call; add CTA
  destination comment (/split)
- SixMonthTrendsWidget: lowercase series names in aria-label to match test
  expectations; replace generic summary with per-month sr-only text
  ('Jul: remittances $2,800, savings $1,200, ...') for richer screen-reader
  output; remove unused buildChartSummary import; add CTA rationale comment
  (no CTA — trends emerge from activity)
- GoalProgress: add CTA destination comment (/goals)
- RecentTransactionsWidget: add CTA destination comment (/send)
- SavingsByGoalWidget: add CTA destination comment (/goals)
- GoalProgress.test.tsx: fix retry-button query to use aria-label
  ('Retry loading data') consistent with WidgetErrorState

Not included: loading/skeleton states (deferred to UX-005)

CTA destinations table (see PR description for full rationale):
| Widget                  | CTA label            | Route  |
|-------------------------|----------------------|--------|
| MoneyDistributionWidget | Set up your split    | /split |
| RecentTransactionsWidget| Send money           | /send  |
| SavingsByGoalWidget     | Create a goal        | /goals |
| GoalProgress            | Add to this goal     | /goals |
| SixMonthTrendsWidget    | (none)               | n/a    |
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.

UI/UX: Design empty, loading, and error states for the dashboard data widgets

1 participant