mirror of
https://github.com/supabase/supabase.git
synced 2026-06-02 19:02:06 +08:00
fix(studio): resolve layout shift on account preferences page (#42680)
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Bug fix ## What is the current behavior? Fixes #34967 The `/account/me` ([https://supabase.com/dashboard/account/me](https://supabase.com/dashboard/account/me)) preferences page has layout shifting during profile loading. ## What is the new behavior? - Loading skeletons now match the structure of the loaded content (one card per dynamic section) - Static sections only render in the non-error branch, preventing them from appearing alongside error messages - Removed unused `isSuccess` destructure from `useProfile()` - CLS value now on good range <= 0.10. <img width="1917" height="905" alt="Screenshot from 2026-02-11 00-39-19" src="https://github.com/user-attachments/assets/1d8fa7e1-9ca3-49ae-b4b8-1c0f28ebbf93" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Enhanced the loading state interface on the account settings page with improved visual indicators. Updated the component structure for better UI consistency and refined the visual feedback mechanism during profile data retrieval. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Card, CardContent } from 'ui'
|
||||
import { Card, CardContent, CardFooter } from 'ui'
|
||||
import { PageContainer } from 'ui-patterns/PageContainer'
|
||||
import {
|
||||
PageHeader,
|
||||
@@ -7,7 +7,15 @@ import {
|
||||
PageHeaderSummary,
|
||||
PageHeaderTitle,
|
||||
} from 'ui-patterns/PageHeader'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
import {
|
||||
PageSection,
|
||||
PageSectionContent,
|
||||
PageSectionDescription,
|
||||
PageSectionMeta,
|
||||
PageSectionSummary,
|
||||
PageSectionTitle,
|
||||
} from 'ui-patterns/PageSection'
|
||||
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { AccountConnections } from '@/components/interfaces/Account/Preferences/AccountConnections'
|
||||
import { AccountDeletion } from '@/components/interfaces/Account/Preferences/AccountDeletion'
|
||||
@@ -58,20 +66,12 @@ const PlatformPreferences = () => {
|
||||
'profile:show_analytics_and_marketing',
|
||||
'profile:show_account_deletion',
|
||||
])
|
||||
const { error, isLoading, isError, isSuccess } = useProfile()
|
||||
const { error, isLoading, isError } = useProfile()
|
||||
|
||||
return (
|
||||
<>
|
||||
<PreferencesPageHeader description="Manage your account profile, connections, and dashboard experience." />
|
||||
<PageContainer size="small">
|
||||
{isLoading && (
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
<GenericSkeletonLoader />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{isError && (
|
||||
<Card>
|
||||
<CardContent className="p-4">
|
||||
@@ -80,29 +80,110 @@ const PlatformPreferences = () => {
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{isSuccess && (
|
||||
{!isError && (
|
||||
<>
|
||||
{profileShowInformation ? <ProfileInformation /> : null}
|
||||
<AccountIdentities />
|
||||
{isLoading ? (
|
||||
<ProfileLoadingSections showProfileInformation={profileShowInformation} />
|
||||
) : (
|
||||
<>
|
||||
{profileShowInformation ? <ProfileInformation /> : null}
|
||||
<AccountIdentities />
|
||||
</>
|
||||
)}
|
||||
|
||||
<AccountConnections />
|
||||
|
||||
<ThemeSettings />
|
||||
|
||||
<HotkeySettings />
|
||||
|
||||
<DashboardSettings />
|
||||
|
||||
{profileShowAnalyticsAndMarketing && <AnalyticsSettings />}
|
||||
|
||||
{profileShowAccountDeletion && <AccountDeletion />}
|
||||
</>
|
||||
)}
|
||||
|
||||
<AccountConnections />
|
||||
|
||||
<ThemeSettings />
|
||||
|
||||
<HotkeySettings />
|
||||
|
||||
<DashboardSettings />
|
||||
|
||||
{profileShowAnalyticsAndMarketing && <AnalyticsSettings />}
|
||||
|
||||
{profileShowAccountDeletion && <AccountDeletion />}
|
||||
</PageContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const ProfileLoadingSections = ({
|
||||
showProfileInformation,
|
||||
}: {
|
||||
showProfileInformation: boolean
|
||||
}) => (
|
||||
<>
|
||||
{showProfileInformation && (
|
||||
<PageSection>
|
||||
<PageSectionMeta>
|
||||
<PageSectionSummary>
|
||||
<PageSectionTitle>Profile information</PageSectionTitle>
|
||||
</PageSectionSummary>
|
||||
</PageSectionMeta>
|
||||
<PageSectionContent>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<ProfileFieldLoadingRow labelWidth="w-24" />
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<ProfileFieldLoadingRow labelWidth="w-20" />
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<ProfileFieldLoadingRow labelWidth="w-28" descriptionWidth="w-48" />
|
||||
</CardContent>
|
||||
<CardContent>
|
||||
<ProfileFieldLoadingRow labelWidth="w-20" descriptionWidth="w-40" />
|
||||
</CardContent>
|
||||
<CardFooter className="justify-end">
|
||||
<ShimmeringLoader className="h-8 w-16 rounded-md py-0" />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</PageSectionContent>
|
||||
</PageSection>
|
||||
)}
|
||||
|
||||
<PageSection>
|
||||
<PageSectionMeta>
|
||||
<PageSectionSummary>
|
||||
<PageSectionTitle>Account identities</PageSectionTitle>
|
||||
<PageSectionDescription>
|
||||
Manage the providers linked to your Supabase account and update their details.
|
||||
</PageSectionDescription>
|
||||
</PageSectionSummary>
|
||||
</PageSectionMeta>
|
||||
<PageSectionContent>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<ShimmeringLoader />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</PageSectionContent>
|
||||
</PageSection>
|
||||
</>
|
||||
)
|
||||
|
||||
const ProfileFieldLoadingRow = ({
|
||||
labelWidth,
|
||||
descriptionWidth,
|
||||
}: {
|
||||
labelWidth: string
|
||||
descriptionWidth?: string
|
||||
}) => (
|
||||
<div className="flex flex-col-reverse gap-2 md:gap-6 md:flex-row-reverse md:justify-between">
|
||||
<div className="flex flex-col justify-center items-start md:items-end shrink-0 md:w-1/2 xl:w-2/5 md:min-w-100">
|
||||
<ShimmeringLoader className="h-8 w-full rounded-md py-0" />
|
||||
</div>
|
||||
<div className="flex flex-col min-w-0 flex-grow">
|
||||
<ShimmeringLoader className={`${labelWidth} h-4 py-0`} />
|
||||
{descriptionWidth !== undefined && (
|
||||
<ShimmeringLoader className={`${descriptionWidth} mt-2 h-3 py-0`} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const SelfHostedPreferences = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user