feat(ui): standardize loading and empty states across primary data pages - #31
Merged
Meshmulla merged 1 commit intoAug 26, 2026
Conversation
Wire up the existing EmptyState/Skeleton primitives (previously unused outside of Storybook) into Bridges, Watchlist, Alerts, Incidents, and Transactions so each page consistently shows a shape-matching loading skeleton and a friendly EmptyState instead of a blank panel or bare text. - EmptyBridges, EmptyAlerts, and EmptyTransactions now accept a hasFilters/onClearFilters pair so "nothing here yet" is visually and textually distinct from "nothing matches your filters", with a clear filters action for the latter. Added a new EmptyIncidents variant. - Bridges.tsx: swap the ad-hoc empty divs for EmptyBridges, wired to the favorites toggle and bridge status filter. - Watchlist.tsx: swap the inline empty block for EmptyWatchlist and use SkeletonText for the price/health cells while asset stats are loading. - CompactAlertList.tsx (Alerts page): distinguish "no active alerts" from "no alerts match your filters/search" via EmptyAlerts, with a clear action that resets severity/status/search/dismissed state. - IncidentHeatmap.tsx (Incidents page): replace the plain "Loading…" text with a skeleton grid matching the heatmap's shape, and add EmptyIncidents for the zero-incidents case. - TransactionHistory.tsx (Transactions page): replace the duplicated desktop/mobile empty blocks with EmptyTransactions, distinguishing an empty result set from an active-filter mismatch. - All EmptyState copy used by these pages now runs through react-i18next (new emptyStates.* keys + common.clearFilters), translated across all 8 supported locales to satisfy the locale-completeness test. - Reduced-motion is already handled globally for .skeleton animations via index.css, so no additional per-component work was needed there. - Added tests covering the empty/filtered rendering of the EmptyState variants and the Watchlist page's empty state. Closes stellar-kracken#29
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #29
Wires up the existing
EmptyState/Skeletoncomponent primitives (already built undersrc/components/EmptyStateandsrc/components/Skeleton, but not consumed anywhere outside Storybook) into the primary data pages, so each shows a shape-matching loading skeleton and a friendly empty state instead of a blank panel, a bare spinner, or ad-hoc placeholder text.Pages updated
src/pages/Bridges.tsx) — swapped the ad-hoc empty<div>s forEmptyBridges, wired to the favorites toggle and bridge status filter.src/pages/Watchlist.tsx) — swapped the inline empty block forEmptyWatchlist; addedSkeletonTextto the price/health table cells while per-asset stats are loading.src/components/alerts/CompactAlertList.tsx, rendered byAlerts.tsx) — distinguishes "no active alerts" from "no alerts match your filters/search" viaEmptyAlerts, with a clear-filters action that resets severity/status/search/dismissed state.src/components/IncidentHeatmap.tsx, rendered byIncidents.tsx) — replaced the plain "Loading…" text with a skeleton grid matching the heatmap's shape, and added a newEmptyIncidentsvariant for the zero-incidents case.src/components/TransactionHistory.tsx, rendered byTransactions.tsx) — replaced the duplicated desktop/mobile empty blocks withEmptyTransactions, distinguishing a genuinely empty result set from an active-filter mismatch.Empty vs. no-matches
EmptyBridges,EmptyAlerts, andEmptyTransactionsnow accept ahasFilters/onClearFilterspair so "nothing here yet" is visually and textually distinct from "nothing matches your current filter", with a "Clear filters" action for the latter case.i18n
All empty-state copy consumed by these pages now runs through
react-i18next(newemptyStates.*keys pluscommon.clearFilters), translated across all 8 supported locales (en, es, fr, de, zh, ja, ko, ar) to keepsrc/i18n/localeCompleteness.test.tsgreen.Reduced motion
The
.skeletonshimmer animation is already gated globally insrc/index.css(@media (prefers-reduced-motion: reduce)zeroes all animation/transition durations), so no additional per-component work was needed for this criterion — new skeleton markup reuses the same.skeletonclass.Tests
src/components/EmptyState/variants.test.tsx— covers the empty vs. filtered rendering (and copy) ofEmptyBridges,EmptyAlerts,EmptyIncidents,EmptyTransactions,EmptyWatchlist.src/pages/Watchlist.test.tsx— covers the Watchlist page's empty state and its disappearance once an asset is added.Testing/validation performed
npx tsc -p tsconfig.json --noEmit— passesnpx eslint src/ --ext .ts,.tsx— passes, no warningsnpx vitest run— full suite passes: 611 tests / 58 files, including the locale-completeness test across all 8 localesnpm run build— production build succeedsNotes / follow-ups
package-lock.jsonhad a pre-existing local diff unrelated to this change (a storybook addon version) and was intentionally left out of this PR.