diff --git a/frontend/src/components/gallery/PhotoFilterBar.tsx b/frontend/src/components/gallery/PhotoFilterBar.tsx index c89fc71f..239cc850 100644 --- a/frontend/src/components/gallery/PhotoFilterBar.tsx +++ b/frontend/src/components/gallery/PhotoFilterBar.tsx @@ -154,39 +154,45 @@ export const PhotoFilterBar: React.FC = ({ {/* Category and Feedback Filters */}
- {/* Categories Row */} - {categories && categories.length > 0 && ( + {/* Categories + desktop feedback row. Rendered whenever EITHER part + has content: the desktop feedback chips must not depend on the + (optional) categories existing, or category-less galleries show + no feedback filter at all on desktop (#802 — the lg:hidden + fallback block below only covers mobile/tablet). */} + {((categories && categories.length > 0) || (feedbackEnabled && !!onFilterChange)) && (
{/* Categories: keep in a horizontal scroll container */} -
-
- - {categories.map((category) => { - const categoryPhotoCount = photos.filter(p => p.category_id === category.id).length; - if (categoryPhotoCount === 0) return null; - - return ( - - ); - })} + {categories && categories.length > 0 && ( +
+
+ + {categories.map((category) => { + const categoryPhotoCount = photos.filter(p => p.category_id === category.id).length; + if (categoryPhotoCount === 0) return null; + + return ( + + ); + })} +
-
+ )} {/* Desktop: compact horizontal feedback filter with headline (icons only) */} {feedbackEnabled && onFilterChange && ( @@ -244,7 +250,10 @@ export const PhotoFilterBar: React.FC = ({
)} -

+ {/* Without categories this row only carries desktop content (the + chips are lg-only; mobile has its own block below), so hide + the count below lg to keep the mobile layout unchanged. */} +

0 ? '' : 'hidden lg:block'}`}> {photoCount} {t('common.media', 'media')}

diff --git a/frontend/src/components/gallery/__tests__/PhotoFilterBar.feedbackChips.test.tsx b/frontend/src/components/gallery/__tests__/PhotoFilterBar.feedbackChips.test.tsx new file mode 100644 index 00000000..17b7361b --- /dev/null +++ b/frontend/src/components/gallery/__tests__/PhotoFilterBar.feedbackChips.test.tsx @@ -0,0 +1,72 @@ +/** + * Regression coverage for #802: the desktop feedback-filter chips + * (All / Likes / Saved / Rated / Commented) were nested inside the + * categories row, so a gallery WITHOUT photo categories (the default) + * rendered no feedback filter at all on desktop — the standalone + * fallback block is lg:hidden (mobile/tablet only). These tests pin + * that both chip groups exist in the DOM regardless of categories. + */ +import { render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import { PhotoFilterBar } from '../PhotoFilterBar'; + +vi.mock('react-i18next', async () => { + const actual = await vi.importActual('react-i18next'); + return { + ...actual, + useTranslation: () => ({ + t: (_key: string, fallback?: unknown) => + typeof fallback === 'string' ? fallback : _key, + i18n: { language: 'en' } + }) + }; +}); + +const baseProps = { + categories: [] as Array<{ id: number; name: string; slug: string }>, + photos: [] as never[], + selectedCategoryId: null, + onCategoryChange: vi.fn(), + searchTerm: '', + onSearchChange: vi.fn(), + sortBy: 'date' as const, + onSortChange: vi.fn(), + photoCount: 0, +}; + +describe('PhotoFilterBar feedback chips (#802)', () => { + it('renders both chip groups (desktop lg:flex + mobile lg:hidden) with NO categories', () => { + render( + + ); + // Two groups: the desktop row variant and the mobile fallback. Before + // the fix, only the mobile one rendered when categories were empty, + // leaving desktop with no feedback filter at all. + expect(screen.getAllByText('Feedback Filter')).toHaveLength(2); + }); + + it('still renders both chip groups when categories exist', () => { + render( + + ); + expect(screen.getAllByText('Feedback Filter')).toHaveLength(2); + }); + + it('renders no chips when feedback is disabled', () => { + render(); + expect(screen.queryByText('Feedback Filter')).toBeNull(); + }); +});