fix(web): clean up header logo accessibility (#36567)

This commit is contained in:
yyh 2026-05-25 10:22:34 +08:00 committed by GitHub
parent ffd336cfe8
commit 1e9c94b788
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 47 additions and 29 deletions

View File

@ -5,6 +5,7 @@ import { useSuspenseQuery } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import Link from '@/next/link'
import { useRouter } from '@/next/navigation'
import { systemFeaturesQueryOptions } from '@/service/system-features'
import Avatar from './avatar'
@ -17,21 +18,26 @@ const Header = () => {
const goToStudio = useCallback(() => {
router.push('/apps')
}, [router])
const logoLabel = systemFeatures.branding.enabled && systemFeatures.branding.application_title ? systemFeatures.branding.application_title : 'Dify'
return (
<div className="flex flex-1 items-center justify-between px-4">
<div className="flex items-center gap-3">
<div className="flex cursor-pointer items-center" onClick={goToStudio}>
<Link
href="/apps"
className="flex items-center rounded-sm hover:opacity-80 focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:outline-hidden"
aria-label={logoLabel}
>
{systemFeatures.branding.enabled && systemFeatures.branding.login_page_logo
? (
<img
src={systemFeatures.branding.login_page_logo}
className="block h-[22px] w-auto object-contain"
alt="Dify logo"
alt=""
/>
)
: <DifyLogo />}
</div>
: <DifyLogo alt="" />}
</Link>
<div className="h-4 w-px origin-center rotate-[11.31deg] bg-divider-regular" />
<p className="relative mt-[-2px] title-3xl-semi-bold text-text-primary">{t('account.account', { ns: 'common' })}</p>
</div>

View File

@ -58,6 +58,12 @@ describe('DifyLogo', () => {
const img = screen.getByRole('img', { name: /dify logo/i })
expect(img).toHaveClass('custom-test-class')
})
it('applies custom alt text', () => {
const { container } = render(<DifyLogo alt="" />)
const img = container.querySelector('img')
expect(img).toHaveAttribute('alt', '')
})
})
describe('Theme behavior', () => {

View File

@ -23,12 +23,14 @@ type DifyLogoProps = {
style?: LogoStyle
size?: LogoSize
className?: string
alt?: string
}
const DifyLogo: FC<DifyLogoProps> = ({
style = 'default',
size = 'medium',
className,
alt = 'Dify logo',
}) => {
const { theme } = useTheme()
const themedStyle = (theme === 'dark' && style === 'default') ? 'monochromeWhite' : style
@ -37,7 +39,7 @@ const DifyLogo: FC<DifyLogoProps> = ({
<img
src={`${basePath}${logoPathMap[themedStyle]}`}
className={cn('block object-contain', logoSizeMap[size], className)}
alt="Dify logo"
alt={alt}
/>
)
}

View File

@ -1,4 +1,4 @@
import type { ReactElement } from 'react'
import type { AnchorHTMLAttributes, ReactElement } from 'react'
import { fireEvent, screen } from '@testing-library/react'
import { vi } from 'vitest'
import { renderWithSystemFeatures } from '@/__tests__/utils/mock-system-features'
@ -55,7 +55,7 @@ vi.mock('@/context/workspace-context-provider', () => ({
}))
vi.mock('@/next/link', () => ({
default: ({ children, href }: { children?: React.ReactNode, href?: string }) => <a href={href}>{children}</a>,
default: ({ children, href, ...props }: AnchorHTMLAttributes<HTMLAnchorElement> & { href?: string }) => <a href={href} {...props}>{children}</a>,
}))
let mockIsWorkspaceEditor = false
@ -122,7 +122,9 @@ describe('Header', () => {
it('should render header with main nav components', () => {
renderHeader()
expect(screen.getByRole('img', { name: /dify logo/i })).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'Dify' })).toHaveAttribute('href', '/apps')
expect(screen.queryByRole('heading', { level: 1 })).not.toBeInTheDocument()
expect(screen.queryByRole('img', { name: /dify logo/i })).not.toBeInTheDocument()
expect(screen.getByTestId('workplace-selector')).toBeInTheDocument()
expect(screen.getByTestId('app-nav')).toBeInTheDocument()
expect(screen.getByTestId('account-dropdown')).toBeInTheDocument()
@ -166,7 +168,7 @@ describe('Header', () => {
mockMedia = 'mobile'
renderHeader()
expect(screen.getByRole('img', { name: /dify logo/i })).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'Dify' })).toHaveAttribute('href', '/apps')
expect(screen.queryByTestId('env-nav')).not.toBeInTheDocument()
})
@ -177,8 +179,8 @@ describe('Header', () => {
renderHeader()
expect(screen.getByText('Acme Workspace')).toBeInTheDocument()
expect(screen.getByRole('img', { name: /logo/i })).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'Acme Workspace' })).toHaveAttribute('href', '/apps')
expect(screen.queryByRole('img', { name: /logo/i })).not.toBeInTheDocument()
expect(screen.queryByRole('img', { name: /dify logo/i })).not.toBeInTheDocument()
})
@ -189,18 +191,18 @@ describe('Header', () => {
renderHeader()
expect(screen.getByText('Custom Title')).toBeInTheDocument()
expect(screen.getByRole('img', { name: /dify logo/i })).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'Custom Title' })).toHaveAttribute('href', '/apps')
expect(screen.queryByRole('img', { name: /dify logo/i })).not.toBeInTheDocument()
})
it('should show default Dify text when branding enabled but no application_title', () => {
it('should use default Dify link label when branding enabled but no application_title', () => {
mockBrandingEnabled = true
mockBrandingTitle = null
mockBrandingLogo = null
renderHeader()
expect(screen.getByText('Dify')).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'Dify' })).toHaveAttribute('href', '/apps')
})
it('should show dataset nav for editor who is not dataset operator', () => {

View File

@ -44,21 +44,23 @@ const Header = () => {
setShowAccountSettingModal({ payload: ACCOUNT_SETTING_TAB.BILLING })
}, [isFreePlan, setShowAccountSettingModal, setShowPricingModal])
const logoLabel = isBrandingEnabled && systemFeatures.branding.application_title ? systemFeatures.branding.application_title : 'Dify'
const renderLogo = () => (
<h1>
<Link href="/apps" className="flex h-8 shrink-0 items-center justify-center overflow-hidden px-0.5 indent-[-9999px] whitespace-nowrap">
{isBrandingEnabled && systemFeatures.branding.application_title ? systemFeatures.branding.application_title : 'Dify'}
{systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? (
<img
src={systemFeatures.branding.workspace_logo}
className="block h-[22px] w-auto object-contain"
alt="logo"
/>
)
: <DifyLogo />}
</Link>
</h1>
<Link
href="/apps"
className="flex h-8 shrink-0 items-center justify-center overflow-hidden rounded-sm px-0.5 hover:opacity-80 focus-visible:ring-1 focus-visible:ring-components-input-border-active focus-visible:outline-hidden"
aria-label={logoLabel}
>
{systemFeatures.branding.enabled && systemFeatures.branding.workspace_logo
? (
<img
src={systemFeatures.branding.workspace_logo}
className="block h-[22px] w-auto object-contain"
alt=""
/>
)
: <DifyLogo alt="" />}
</Link>
)
if (isMobile) {