fix(web): remove Web Apps loading skeleton (#39939)

This commit is contained in:
yyh
2026-08-03 13:41:29 +00:00
committed by GitHub
parent 984045190c
commit a77236e564
2 changed files with 49 additions and 88 deletions
@@ -1335,15 +1335,14 @@ describe('MainNav', () => {
).toHaveAttribute('href', '/installed/installed-2')
})
it('renders web app skeleton rows while installed apps are loading', () => {
it('hides the installed web apps section while installed apps are loading', () => {
mockInstalledAppsPending = true
renderMainNav()
expect(screen.getByRole('region', { name: 'explore.sidebar.webApps' })).toHaveAttribute(
'aria-busy',
'true',
)
expect(
screen.queryByRole('region', { name: 'explore.sidebar.webApps' }),
).not.toBeInTheDocument()
expect(
screen.queryByRole('button', { name: 'explore.sidebar.webApps' }),
).not.toBeInTheDocument()
@@ -38,39 +38,11 @@ import { usePathname } from '@/next/navigation'
import { consoleQuery } from '@/service/client'
import { hasPermission } from '@/utils/permission'
const webAppSkeletonClassName =
'animate-pulse rounded bg-text-quaternary opacity-20 motion-reduce:animate-none'
const webAppSkeletonWidths = ['w-24', 'w-32', 'w-28']
const emptyInstalledApps: InstalledAppResponse[] = []
const selectInstalledApps = (data: InfiniteData<InstalledAppListResponse, string | undefined>) =>
data.pages.flatMap((page) => page.installed_apps)
function WebAppsHeaderSkeleton() {
return (
<div aria-hidden="true" className="flex h-8 items-center justify-between p-2">
<div className={cn(webAppSkeletonClassName, 'h-3 w-20')} />
<div className={cn(webAppSkeletonClassName, 'size-4 rounded-md')} />
</div>
)
}
function WebAppsSkeleton() {
return (
<div aria-hidden="true" className="space-y-0.5 pb-2">
{webAppSkeletonWidths.map((width) => (
<div key={width} className="flex h-8 items-center gap-2 rounded-lg py-0.5 pr-0.5 pl-2">
<div className={cn(webAppSkeletonClassName, 'size-5 shrink-0 rounded-md')} />
<div className="min-w-0 flex-1 py-1 pr-1">
<div className={cn(webAppSkeletonClassName, 'h-3', width)} />
</div>
<div className={cn(webAppSkeletonClassName, 'mr-1 h-3 w-3 shrink-0')} />
</div>
))}
</div>
)
}
const WebAppsSectionContent = () => {
const { t } = useTranslation()
const pathname = usePathname()
@@ -135,12 +107,9 @@ const WebAppsSectionContent = () => {
)
}
if (
!installedAppsQuery.isPending &&
!installedAppsQuery.isError &&
installedApps.length === 0 &&
!normalizedSearchText
)
if (installedAppsQuery.isPending) return null
if (!installedAppsQuery.isError && installedApps.length === 0 && !normalizedSearchText)
return null
const renderAppNavItem = (installedApp: (typeof installedApps)[number]) => (
@@ -159,49 +128,45 @@ const WebAppsSectionContent = () => {
)
return (
<div className="flex min-h-0 flex-1 flex-col">
{installedAppsQuery.isPending ? (
<WebAppsHeaderSkeleton />
) : (
<div className="flex items-center justify-between py-1 pr-2 pl-2">
<div className="flex items-center justify-between py-1 pr-2 pl-2">
<button
type="button"
aria-expanded={appsExpanded}
className="flex min-w-0 items-center rounded-md px-2 py-1 text-left system-xs-medium-uppercase text-text-tertiary outline-hidden hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid"
onClick={() => setAppsExpanded((value) => !value)}
>
<span>{t(($) => $['sidebar.webApps'], { ns: 'explore' })}</span>
<span
aria-hidden
className={cn(
'i-ri-arrow-down-s-fill h-4 w-4 shrink-0 transition-transform',
!appsExpanded && '-rotate-90',
)}
/>
</button>
<div className="flex items-center gap-0.5">
<button
type="button"
aria-expanded={appsExpanded}
className="flex min-w-0 items-center rounded-md px-2 py-1 text-left system-xs-medium-uppercase text-text-tertiary outline-hidden hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid"
onClick={() => setAppsExpanded((value) => !value)}
aria-label={t(($) => $['operation.search'], { ns: 'common' })}
className={cn(
'flex h-6 w-6 items-center justify-center rounded-md p-0.5 text-text-tertiary outline-hidden hover:bg-state-base-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid',
searchVisible && 'bg-state-base-hover text-text-secondary',
)}
onClick={() => {
setAppsExpanded(true)
setSearchVisible((value) => {
if (value) setSearchText('')
return !value
})
}}
>
<span>{t(($) => $['sidebar.webApps'], { ns: 'explore' })}</span>
<span
aria-hidden
className={cn(
'i-ri-arrow-down-s-fill h-4 w-4 shrink-0 transition-transform',
!appsExpanded && '-rotate-90',
)}
/>
<span className="flex size-5 shrink-0 items-center justify-center">
<span aria-hidden className="i-ri-search-line size-3.5" />
</span>
</button>
<div className="flex items-center gap-0.5">
<button
type="button"
aria-label={t(($) => $['operation.search'], { ns: 'common' })}
className={cn(
'flex h-6 w-6 items-center justify-center rounded-md p-0.5 text-text-tertiary outline-hidden hover:bg-state-base-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid',
searchVisible && 'bg-state-base-hover text-text-secondary',
)}
onClick={() => {
setAppsExpanded(true)
setSearchVisible((value) => {
if (value) setSearchText('')
return !value
})
}}
>
<span className="flex size-5 shrink-0 items-center justify-center">
<span aria-hidden className="i-ri-search-line size-3.5" />
</span>
</button>
</div>
</div>
)}
{!installedAppsQuery.isPending && appsExpanded && searchVisible && (
</div>
{appsExpanded && searchVisible && (
<div className="px-2 pb-2">
<SearchInput
value={searchText}
@@ -216,15 +181,14 @@ const WebAppsSectionContent = () => {
<ScrollArea className="relative min-h-0 flex-1 overflow-hidden">
<ScrollAreaViewport
ref={scrollRef}
aria-busy={installedAppsQuery.isPending || installedAppsQuery.isFetchingNextPage}
aria-busy={installedAppsQuery.isFetchingNextPage}
aria-label={t(($) => $['sidebar.webApps'], { ns: 'explore' })}
style={{ overflowX: 'hidden' }}
className="overscroll-contain"
role="region"
>
<ScrollAreaContent style={{ minWidth: 0 }} className="w-full max-w-full px-2">
{installedAppsQuery.isPending && <WebAppsSkeleton />}
{!installedAppsQuery.isPending && installedAppsQuery.isError && (
{installedAppsQuery.isError && (
<div
className="flex flex-col items-start gap-1 px-2 py-2 system-xs-regular text-text-tertiary"
role="alert"
@@ -243,14 +207,12 @@ const WebAppsSectionContent = () => {
</button>
</div>
)}
{!installedAppsQuery.isPending &&
!installedAppsQuery.isError &&
installedApps.length === 0 && (
<div className="px-2 py-1 system-xs-regular">
{t(($) => $['mainNav.webApps.noResults'], { ns: 'common' })}
</div>
)}
{!installedAppsQuery.isPending && installedApps.length > 0 && (
{!installedAppsQuery.isError && installedApps.length === 0 && (
<div className="px-2 py-1 system-xs-regular">
{t(($) => $['mainNav.webApps.noResults'], { ns: 'common' })}
</div>
)}
{installedApps.length > 0 && (
<div className="space-y-0.5 pb-2">
{installedApps.map((installedApp, index) => (
<Fragment key={installedApp.id}>