Repository navigation
feat(loading): add content-shaped skeletons for every route #467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
9c84df9
ux(loading): reveal skeletons after a delay and keep card heights stable
yhabib e991e17
ux(loading): add content-shaped skeletons for every route
yhabib 6bea717
Merge branch 'main' of github.com:dfinity/governance-app into ux/rout…
yhabib d9f6517
fix(loading): match the voting, settings, and neurons skeletons to th…
yhabib 1d62088
fix(loading): match the neuron card, proposal, and transaction skeletons
yhabib 574635a
fix(loading): match the voting shell to the advanced following overview
yhabib 4254859
fix(loading): match the dashboard account card to the subaccounts flag
yhabib File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
15 changes: 0 additions & 15 deletions
15
src/governance-app-frontend/src/common/components/MultipleSkeletons.tsx
This file was deleted.
Oops, something went wrong.
49 changes: 49 additions & 0 deletions
49
src/governance-app-frontend/src/common/components/skeletons/AccountsSkeleton.tsx
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { Card, CardContent, CardHeader } from '@components/Card'; | ||
| import { Skeleton } from '@components/Skeleton'; | ||
|
|
||
| import { SkeletonAccountCard } from './SkeletonAccountCard'; | ||
| import { SkeletonPageHeader } from './SkeletonPageHeader'; | ||
| import { SkeletonTransactionRows } from './SkeletonTransactionList'; | ||
|
|
||
| /** | ||
| * Mirrors the `/accounts` layout: total card, account list, recent activity. | ||
| * | ||
| * `SkeletonTransactionRows` inside announces the load, so this wrapper stays a | ||
| * plain `div`. Nested status regions read twice. | ||
| */ | ||
| export const AccountsSkeleton = () => ( | ||
| <div className="flex flex-col gap-6"> | ||
| <SkeletonPageHeader action={true} /> | ||
|
|
||
| <Card> | ||
| <CardHeader className="flex flex-col gap-2"> | ||
| <Skeleton className="h-5 w-40" /> | ||
| <Skeleton className="h-8 w-32" /> | ||
| <Skeleton className="h-5 w-20" /> | ||
| </CardHeader> | ||
| <CardContent className="flex flex-col gap-3"> | ||
| <Skeleton className="h-3 w-full rounded-full" /> | ||
| <div className="flex gap-4"> | ||
| <Skeleton className="h-5 w-24" /> | ||
| <Skeleton className="h-5 w-24" /> | ||
| </div> | ||
| </CardContent> | ||
| </Card> | ||
|
|
||
| <div className="grid grid-cols-1 gap-6 lg:grid-cols-3"> | ||
| <div className="flex flex-col gap-4 lg:col-span-2"> | ||
| <SkeletonAccountCard /> | ||
| <SkeletonAccountCard /> | ||
| </div> | ||
|
|
||
| <Card className="h-fit"> | ||
| <CardHeader> | ||
| <Skeleton className="h-5 w-40" /> | ||
| </CardHeader> | ||
| <CardContent> | ||
| <SkeletonTransactionRows /> | ||
| </CardContent> | ||
| </Card> | ||
| </div> | ||
| </div> | ||
| ); |
86 changes: 86 additions & 0 deletions
86
src/governance-app-frontend/src/common/components/skeletons/DashboardSkeleton.tsx
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| import { Card, CardContent, CardHeader } from '@components/Card'; | ||
| import { Skeleton } from '@components/Skeleton'; | ||
|
|
||
| import { SkeletonScreen } from './SkeletonScreen'; | ||
| import { SkeletonStatCard } from './SkeletonStatCard'; | ||
| import { SkeletonText } from './SkeletonText'; | ||
|
|
||
| /** Mirrors the `/dashboard` layout, so the real page lands in the same frame. */ | ||
| export const DashboardSkeleton = () => ( | ||
| <SkeletonScreen className="flex flex-col gap-8"> | ||
| {/* SmartTitle */} | ||
| <div className="flex flex-col gap-2"> | ||
| <Skeleton className="h-10 w-full max-w-xl" /> | ||
| <Skeleton className="h-10 w-full max-w-md" /> | ||
| </div> | ||
|
|
||
| <div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4"> | ||
| {/* TotalAssetsCard, with its radial chart. */} | ||
| <Card className="items-center pt-4 pb-6"> | ||
| <CardHeader className="flex flex-col items-center gap-2"> | ||
| <Skeleton className="h-5 w-28" /> | ||
| <Skeleton className="h-8 w-40" /> | ||
| </CardHeader> | ||
| <CardContent className="flex w-full justify-center"> | ||
| <Skeleton className="aspect-square w-full max-w-48 rounded-full" /> | ||
| </CardContent> | ||
| </Card> | ||
|
|
||
| {/* AccountCard or AccountsCard. */} | ||
| <Card className="pt-4 pb-6"> | ||
| <CardHeader className="flex flex-col gap-2"> | ||
| <Skeleton className="h-5 w-24" /> | ||
| <Skeleton className="h-8 w-32" /> | ||
| <Skeleton className="h-5 w-20" /> | ||
| </CardHeader> | ||
| <CardContent className="flex flex-col gap-3"> | ||
| <Skeleton className="h-12 w-full" /> | ||
| </CardContent> | ||
| </Card> | ||
|
|
||
| {/* StakedCard. */} | ||
| <Card className="pt-4 pb-6 md:col-span-2"> | ||
| <CardHeader className="flex flex-col gap-2"> | ||
| <div className="flex items-center justify-between"> | ||
| <Skeleton className="h-5 w-24" /> | ||
| <Skeleton className="h-5 w-16" /> | ||
| </div> | ||
| <Skeleton className="h-8 w-32" /> | ||
| <Skeleton className="h-5 w-20" /> | ||
| </CardHeader> | ||
| <CardContent className="flex flex-col gap-6"> | ||
| <div className="grid grid-cols-2 gap-6 border-t pt-4"> | ||
| <Skeleton className="h-7 w-20 justify-self-end" /> | ||
| <Skeleton className="h-7 w-20 justify-self-end" /> | ||
| </div> | ||
| <div className="grid grid-cols-1 gap-4 md:grid-cols-2"> | ||
| <Skeleton className="h-12 w-full" /> | ||
| <Skeleton className="h-12 w-full" /> | ||
| </div> | ||
| </CardContent> | ||
| </Card> | ||
| </div> | ||
|
|
||
| <div className="mt-4 flex flex-col gap-3"> | ||
| <Skeleton className="h-9 w-64" /> | ||
| <div className="grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4"> | ||
| <SkeletonStatCard /> | ||
| <SkeletonStatCard /> | ||
| <SkeletonStatCard /> | ||
| <SkeletonStatCard caption={false} /> | ||
| </div> | ||
|
|
||
| {/* ExecutiveSummaryCard. */} | ||
| <Card> | ||
| <CardHeader className="flex flex-col gap-2"> | ||
| <Skeleton className="h-4 w-40" /> | ||
| <Skeleton className="h-9 w-48" /> | ||
| </CardHeader> | ||
| <CardContent className="grid grid-cols-1 gap-8 md:grid-cols-2"> | ||
| <SkeletonText lines={4} /> | ||
| <SkeletonText lines={4} /> | ||
| </CardContent> | ||
| </Card> | ||
| </div> | ||
| </SkeletonScreen> | ||
| ); |
57 changes: 57 additions & 0 deletions
57
src/governance-app-frontend/src/common/components/skeletons/NeuronsSkeleton.tsx
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { Card, CardContent, CardHeader } from '@components/Card'; | ||
| import { Skeleton } from '@components/Skeleton'; | ||
|
|
||
| import { SkeletonPageHeader } from './SkeletonPageHeader'; | ||
| import { SkeletonScreen } from './SkeletonScreen'; | ||
| import { SkeletonStatCard } from './SkeletonStatCard'; | ||
|
|
||
| /** One card in the neurons grid. */ | ||
| const SkeletonNeuronCard = () => ( | ||
| <Card className="h-full"> | ||
| <CardHeader className="flex flex-col gap-3"> | ||
| <div className="flex items-center justify-between"> | ||
| <Skeleton className="h-5 w-32" /> | ||
| <Skeleton className="h-6 w-20" /> | ||
| </div> | ||
| <Skeleton className="h-8 w-36" /> | ||
| <Skeleton className="h-5 w-24" /> | ||
| </CardHeader> | ||
| <CardContent className="flex flex-col gap-4"> | ||
| <div className="grid grid-cols-2 gap-4 border-t pt-4"> | ||
| <Skeleton className="h-6 w-24" /> | ||
| <Skeleton className="h-6 w-24 justify-self-end" /> | ||
| </div> | ||
| <Skeleton className="h-12 w-full" /> | ||
| </CardContent> | ||
| </Card> | ||
| ); | ||
|
|
||
| /** | ||
| * The summary row and the neuron grid, without the page header. | ||
| * | ||
| * The page keeps its own header while the neurons query runs, so this stands in | ||
| * for the part below it. | ||
| */ | ||
| export const NeuronsContentSkeleton = () => ( | ||
| <SkeletonScreen className="flex flex-col gap-6"> | ||
| <div className="grid grid-cols-2 gap-4 xl:grid-cols-4"> | ||
| <SkeletonStatCard /> | ||
| <SkeletonStatCard /> | ||
| <SkeletonStatCard /> | ||
| <SkeletonStatCard /> | ||
| </div> | ||
|
|
||
| <div className="grid grid-cols-1 gap-4 lg:grid-cols-2"> | ||
| <SkeletonNeuronCard /> | ||
| <SkeletonNeuronCard /> | ||
| </div> | ||
| </SkeletonScreen> | ||
| ); | ||
|
|
||
| /** Mirrors the whole `/neurons` layout. */ | ||
| export const NeuronsSkeleton = () => ( | ||
| <div className="flex flex-col gap-6"> | ||
| <SkeletonPageHeader action={true} /> | ||
| <NeuronsContentSkeleton /> | ||
| </div> | ||
| ); | ||
51 changes: 51 additions & 0 deletions
51
src/governance-app-frontend/src/common/components/skeletons/ProposalDetailSkeleton.tsx
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import { Card, CardContent, CardHeader } from '@components/Card'; | ||
| import { Skeleton } from '@components/Skeleton'; | ||
|
|
||
| import { SkeletonScreen } from './SkeletonScreen'; | ||
| import { SkeletonText } from './SkeletonText'; | ||
|
|
||
| /** | ||
| * The summary card and the body card, without the back link. | ||
| * | ||
| * The page renders its own back link while the proposal query runs, so this | ||
| * stands in for the part below it. | ||
| */ | ||
| export const ProposalDetailContentSkeleton = () => ( | ||
| <SkeletonScreen className="flex flex-col gap-6"> | ||
| <Card> | ||
| <CardHeader className="flex flex-col gap-2"> | ||
| <div className="flex items-center justify-between"> | ||
| <Skeleton className="h-4 w-28" /> | ||
| <Skeleton className="h-4 w-20" /> | ||
| </div> | ||
| <Skeleton className="mt-2 h-9 w-full max-w-2xl" /> | ||
| <div className="flex flex-wrap gap-2"> | ||
| <Skeleton className="h-6 w-20 rounded-full" /> | ||
| <Skeleton className="h-6 w-28 rounded-full" /> | ||
| <Skeleton className="h-6 w-24 rounded-full" /> | ||
| </div> | ||
| </CardHeader> | ||
| <CardContent className="flex flex-col gap-4"> | ||
| <Skeleton className="h-2 w-full rounded-full" /> | ||
| <div className="grid grid-cols-1 gap-4 md:grid-cols-2"> | ||
| <Skeleton className="h-12 w-full" /> | ||
| <Skeleton className="h-12 w-full" /> | ||
| </div> | ||
| </CardContent> | ||
| </Card> | ||
|
|
||
| <Card> | ||
| <CardContent> | ||
| <SkeletonText lines={8} /> | ||
| </CardContent> | ||
|
yhabib marked this conversation as resolved.
Outdated
|
||
| </Card> | ||
| </SkeletonScreen> | ||
| ); | ||
|
|
||
| /** Mirrors a single proposal page: back link, summary card, body card. */ | ||
| export const ProposalDetailSkeleton = () => ( | ||
| <div className="flex flex-col gap-6"> | ||
| <Skeleton className="h-5 w-40" /> | ||
| <ProposalDetailContentSkeleton /> | ||
| </div> | ||
| ); | ||
51 changes: 51 additions & 0 deletions
51
src/governance-app-frontend/src/common/components/skeletons/ProposalListSkeleton.tsx
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import { Card, CardHeader } from '@components/Card'; | ||
| import { Skeleton } from '@components/Skeleton'; | ||
|
|
||
| import { SkeletonScreen } from './SkeletonScreen'; | ||
|
|
||
| type Props = { | ||
| count?: number; | ||
| }; | ||
|
|
||
| /** One row of the proposals list, badges and vote bar included. */ | ||
| const SkeletonProposalCard = () => ( | ||
| <Card className="w-full overflow-hidden"> | ||
| <CardHeader className="flex flex-col gap-2"> | ||
| <div className="flex items-center justify-between"> | ||
| <Skeleton className="h-4 w-28" /> | ||
| <Skeleton className="h-4 w-20" /> | ||
| </div> | ||
|
|
||
| <Skeleton className="h-6 w-full max-w-2xl" /> | ||
|
|
||
| <div className="flex flex-col gap-2 lg:flex-row lg:flex-wrap lg:items-center"> | ||
| <div className="flex flex-wrap items-center gap-2"> | ||
| <Skeleton className="h-6 w-20 rounded-full" /> | ||
| <Skeleton className="h-6 w-28 rounded-full" /> | ||
| <Skeleton className="h-6 w-24 rounded-full" /> | ||
| </div> | ||
| <div className="flex w-full min-w-[200px] flex-1 items-center gap-2 lg:ml-auto lg:w-auto lg:max-w-[500px]"> | ||
| <Skeleton className="h-3 w-8" /> | ||
| <Skeleton className="h-2 flex-grow rounded-full" /> | ||
| <Skeleton className="h-3 w-8" /> | ||
| </div> | ||
| </div> | ||
| </CardHeader> | ||
| </Card> | ||
| ); | ||
|
|
||
| /** The proposal cards alone, for a parent that already announces the load. */ | ||
| export const SkeletonProposalCards = ({ count = 3 }: Props) => ( | ||
| <div className="flex flex-col gap-4"> | ||
| {Array.from({ length: count }).map((_, index) => ( | ||
| <SkeletonProposalCard key={index} /> | ||
| ))} | ||
| </div> | ||
| ); | ||
|
|
||
| /** Stands in for the proposals list, in the page and in the infinite scroll. */ | ||
| export const ProposalListSkeleton = ({ count = 3 }: Props) => ( | ||
| <SkeletonScreen> | ||
| <SkeletonProposalCards count={count} /> | ||
| </SkeletonScreen> | ||
| ); |
76 changes: 76 additions & 0 deletions
76
src/governance-app-frontend/src/common/components/skeletons/SettingsSkeleton.tsx
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import { Card } from '@components/Card'; | ||
| import { Skeleton } from '@components/Skeleton'; | ||
|
|
||
| import { SkeletonPageHeader } from './SkeletonPageHeader'; | ||
| import { SkeletonScreen } from './SkeletonScreen'; | ||
|
|
||
| const SkeletonHeading = ({ description = false }: { description?: boolean }) => ( | ||
| <div className="space-y-2"> | ||
| <Skeleton className="h-8 w-48" /> | ||
| {description && <Skeleton className="h-5 w-80 max-w-full" />} | ||
| </div> | ||
| ); | ||
|
|
||
| const SkeletonRows = ({ rows }: { rows: number }) => ( | ||
| <Card className="overflow-hidden p-0"> | ||
| <div className="flex flex-col divide-y"> | ||
| {Array.from({ length: rows }).map((_, index) => ( | ||
| <div key={index} className="flex items-center justify-between gap-4 px-6 py-5"> | ||
| <div className="flex min-w-0 flex-col gap-2"> | ||
| <Skeleton className="h-5 w-40 max-w-full" /> | ||
| <Skeleton className="h-4 w-64 max-w-full" /> | ||
| </div> | ||
| <Skeleton className="h-8 w-16 shrink-0" /> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </Card> | ||
| ); | ||
|
|
||
| const SkeletonTextCard = () => ( | ||
| <Card className="p-6"> | ||
| <div className="flex flex-col gap-3"> | ||
| <Skeleton className="h-5 w-40 max-w-full" /> | ||
| <Skeleton className="h-4 w-full" /> | ||
| <Skeleton className="h-4 w-3/5" /> | ||
| </div> | ||
| </Card> | ||
| ); | ||
|
|
||
| /** | ||
| * Mirrors the `/settings` sections in order: identity, address book, advanced | ||
| * features, governance, appearance, session, and the version footer. | ||
| */ | ||
| export const SettingsSkeleton = () => ( | ||
| <SkeletonScreen className="flex min-h-full flex-col gap-12"> | ||
| <SkeletonPageHeader /> | ||
| <section className="flex flex-col gap-4"> | ||
| <SkeletonHeading description /> | ||
| <SkeletonRows rows={3} /> | ||
| </section> | ||
| <section className="flex flex-col gap-4"> | ||
| <SkeletonHeading /> | ||
| <SkeletonRows rows={1} /> | ||
| </section> | ||
| <section className="flex flex-col gap-4"> | ||
| <SkeletonHeading /> | ||
| <SkeletonRows rows={4} /> | ||
| </section> | ||
| <section className="flex flex-col gap-4"> | ||
| <SkeletonHeading /> | ||
| <div className="grid grid-cols-1 gap-4 md:grid-cols-2"> | ||
| <SkeletonTextCard /> | ||
| <SkeletonTextCard /> | ||
| </div> | ||
| </section> | ||
| <section className="flex flex-col gap-4"> | ||
| <SkeletonHeading /> | ||
| <SkeletonRows rows={4} /> | ||
| </section> | ||
| <section className="flex flex-col gap-4"> | ||
| <SkeletonHeading description /> | ||
| <Skeleton className="h-10 w-full sm:w-32" /> | ||
| </section> | ||
| <Skeleton className="mt-auto h-4 w-32 self-center" /> | ||
| </SkeletonScreen> | ||
| ); |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.