+
Loading rewards
+
+ {/* Page header */}
+
+
+
+
+ {/* Deferred features notice */}
+
+
+
+ {[0, 1, 2, 3].map((i) => (
+
+ ))}
+
+
+
+ {/* Member count header */}
+
+
+ {/* Member reward cards */}
+
+ {[0, 1, 2].map((i) => (
+
+
+
+
+
+
+ {/* Roles section */}
+
+
+
+ {[0, 1, 2].map((j) => (
+
+ ))}
+
+
+
+
+
+ {/* Deferred sections */}
+
+ {[0, 1, 2, 3].map((k) => (
+
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+ {/* Implementation notes */}
+
+
+
+ {[0, 1, 2].map((i) => (
+
+ ))}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/test/skeleton.test.ts b/test/skeleton.test.ts
index f2952ff..a82c4b7 100644
--- a/test/skeleton.test.ts
+++ b/test/skeleton.test.ts
@@ -5,6 +5,8 @@ import React from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { Skeleton } from '../components/ui/skeleton'
import { MembershipCardSkeleton } from '../components/dashboard/membership-card-skeleton'
+import { AnalyticsSkeleton } from '../components/admin/analytics-skeleton'
+import { RewardsSkeleton } from '../components/admin/rewards-skeleton'
test('skeleton uses theme tokens and stays hidden from assistive technology', () => {
const html = renderToStaticMarkup(
@@ -30,3 +32,36 @@ test('membership skeleton reserves the loaded layout and exposes one loading sta
assert.match(html, /Loading membership details/)
assert.equal((html.match(/aria-hidden="true"/g) ?? []).length, 7)
})
+
+test('analytics skeleton reserves the analytics layout and exposes one loading status', () => {
+ const html = renderToStaticMarkup(
+ React.createElement(AnalyticsSkeleton),
+ )
+
+ assert.match(html, /role="status"/)
+ assert.match(html, /aria-busy="true"/)
+ assert.match(html, /Loading analytics/)
+ // Should have stat cards (3 skeleton cards)
+ assert.equal((html.match(/class="[^"]*rounded-lg border[^"]*"/g) ?? []).length, 6)
+ // Should have skeleton bars inside each card
+ assert.ok(html.includes('h-9'))
+ assert.ok(html.includes('h-2'))
+ // All inner skeleton elements should be hidden from AT
+ assert.ok(html.includes('aria-hidden="true"'))
+})
+
+test('rewards skeleton reserves the rewards layout and exposes one loading status', () => {
+ const html = renderToStaticMarkup(
+ React.createElement(RewardsSkeleton),
+ )
+
+ assert.match(html, /role="status"/)
+ assert.match(html, /aria-busy="true"/)
+ assert.match(html, /Loading rewards/)
+ // Should have member reward cards
+ assert.ok(html.includes('rounded-lg border'))
+ // Should have skeleton elements for roles badges
+ assert.ok(html.includes('rounded-full'))
+ // All inner skeleton elements should be hidden from AT
+ assert.ok(html.includes('aria-hidden="true"'))
+})
diff --git a/test/tsconfig.json b/test/tsconfig.json
index dd5309b..7e09272 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -20,6 +20,8 @@
"../components/ui/skeleton.tsx",
"../components/ui/card.tsx",
"../components/dashboard/membership-card-skeleton.tsx",
+ "../components/admin/analytics-skeleton.tsx",
+ "../components/admin/rewards-skeleton.tsx",
"../lib/api/mappers.ts",
"../lib/api/optimistic.ts",
"../lib/api/types.ts",