diff --git a/src/components/BalanceList.tsx b/src/components/BalanceList.tsx
index 5f9f419..9885082 100644
--- a/src/components/BalanceList.tsx
+++ b/src/components/BalanceList.tsx
@@ -1,7 +1,7 @@
import { useSorokit } from "@/context/useSorokit";
import { Badge } from "@/components/ui/Badge";
import { AssetBadge } from "@/components/AssetBadge";
-import { SkeletonRow } from "@/components/ui/Skeleton";
+import { AssetRowSkeleton } from "@/components/ui/Skeleton";
import type { Balance } from "@/lib/client";
function AssetRow({ b }: { b: Balance }) {
@@ -38,9 +38,9 @@ export function BalanceList() {
Connect your wallet to view assets
) : isLoadingAccount ? (
-
+
{[1, 2, 3].map((i) => (
-
+
))}
) : balances.length === 0 ? (
diff --git a/src/components/index.ts b/src/components/index.ts
index e175083..2899fac 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -10,6 +10,7 @@ export {
} from "./ui/Card";
export { Badge } from "./ui/Badge";
export { Input } from "./ui/Input";
+export { Skeleton, SkeletonRow, SkeletonCard, AssetRowSkeleton } from "./ui/Skeleton";
export {
Skeleton,
SkeletonRow,
diff --git a/src/components/ui/Skeleton.test.tsx b/src/components/ui/Skeleton.test.tsx
index df4460d..e25f99f 100644
--- a/src/components/ui/Skeleton.test.tsx
+++ b/src/components/ui/Skeleton.test.tsx
@@ -1,5 +1,50 @@
import { render, screen } from "@testing-library/react";
import { describe, it, expect } from "vitest";
+import { Skeleton, SkeletonRow, SkeletonCard, AssetRowSkeleton } from "./Skeleton";
+
+describe("Skeleton components accessibility and structure", () => {
+ it("Skeleton has role='presentation'", () => {
+ const { container } = render(
);
+ const el = container.firstChild as HTMLElement;
+ expect(el).toHaveAttribute("role", "presentation");
+ });
+
+ it("SkeletonRow has role='presentation'", () => {
+ const { container } = render(
);
+ const el = container.firstChild as HTMLElement;
+ expect(el).toHaveAttribute("role", "presentation");
+ });
+
+ it("AssetRowSkeleton has role='presentation'", () => {
+ const { container } = render(
);
+ const el = container.firstChild as HTMLElement;
+ expect(el).toHaveAttribute("role", "presentation");
+ });
+
+ it("SkeletonCard has role='status', aria-busy='true', and aria-label='Loading content'", () => {
+ render(
);
+ const el = screen.getByRole("status");
+ expect(el).toHaveAttribute("aria-busy", "true");
+ expect(el).toHaveAttribute("aria-label", "Loading content");
+ });
+
+ it("SkeletonCard accepts custom children override", () => {
+ render(
+
+ Custom Content
+
+ );
+ expect(screen.getByTestId("custom-child")).toBeInTheDocument();
+ expect(screen.getByRole("status")).toHaveAttribute("aria-label", "Loading content");
+ // Default header should not render
+ expect(screen.queryByText("Stellar account details")).not.toBeInTheDocument();
+ });
+
+ it("SkeletonCard accepts custom structure override", () => {
+ render(
+
Custom Structure } />
+ );
+ expect(screen.getByTestId("custom-structure")).toBeInTheDocument();
import {
Skeleton,
SkeletonRow,
diff --git a/src/components/ui/Skeleton.tsx b/src/components/ui/Skeleton.tsx
index fef49e0..63e89eb 100644
--- a/src/components/ui/Skeleton.tsx
+++ b/src/components/ui/Skeleton.tsx
@@ -20,8 +20,13 @@ export function Skeleton({ circle, className, ...props }: SkeletonProps) {
}
/** Pre-composed row skeleton: icon + two lines of text */
-export function SkeletonRow({ className }: { className?: string }) {
+export function SkeletonRow({ className, ...props }: { className?: string; [key: string]: any }) {
return (
+