mirror of
https://github.com/langgenius/dify.git
synced 2026-09-24 23:22:26 +08:00
fix(web): remove Web Apps loading skeleton (#39939)
This commit is contained in:
@@ -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}>
|
||||
|
||||
Reference in New Issue
Block a user