fix(gallery): show feedback filter chips on desktop for galleries without categories (#802)
The desktop feedback-filter chips (All/Likes/Saved/Rated/Commented) were nested inside the categories row conditional, and the standalone fallback block is lg:hidden — so a gallery without photo categories (the default) rendered no feedback filter at all on desktop, despite the docs and a fully working filter implementation behind it. Render the row whenever either part has content and gate only the category scroller on categories existing. The media-count label hides below lg when no categories exist so the mobile layout stays unchanged (mobile keeps its own chip block). With-categories galleries render identically to before. Regression test pins both chip groups in the DOM with and without categories (fails on the pre-fix component).
This commit is contained in:
@@ -154,10 +154,15 @@ export const PhotoFilterBar: React.FC<PhotoFilterBarProps> = ({
|
||||
|
||||
{/* Category and Feedback Filters */}
|
||||
<div className="space-y-3">
|
||||
{/* 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)) && (
|
||||
<div className="flex items-start lg:items-center justify-between flex-col lg:flex-row gap-3">
|
||||
{/* Categories: keep in a horizontal scroll container */}
|
||||
{categories && categories.length > 0 && (
|
||||
<div className="w-full overflow-x-auto pb-2 lg:pb-0">
|
||||
<div className="flex items-center gap-2 min-w-max">
|
||||
<Button
|
||||
@@ -187,6 +192,7 @@ export const PhotoFilterBar: React.FC<PhotoFilterBarProps> = ({
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Desktop: compact horizontal feedback filter with headline (icons only) */}
|
||||
{feedbackEnabled && onFilterChange && (
|
||||
@@ -244,7 +250,10 @@ export const PhotoFilterBar: React.FC<PhotoFilterBarProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="text-xs md:text-sm text-muted-theme flex-shrink-0 ml-auto">
|
||||
{/* 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. */}
|
||||
<p className={`text-xs md:text-sm text-muted-theme flex-shrink-0 ml-auto ${categories && categories.length > 0 ? '' : 'hidden lg:block'}`}>
|
||||
{photoCount} {t('common.media', 'media')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -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<typeof import('react-i18next')>('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(
|
||||
<PhotoFilterBar
|
||||
{...baseProps}
|
||||
feedbackEnabled
|
||||
currentFilter="all"
|
||||
onFilterChange={vi.fn()}
|
||||
/>
|
||||
);
|
||||
// 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(
|
||||
<PhotoFilterBar
|
||||
{...baseProps}
|
||||
categories={[{ id: 1, name: 'Ceremony', slug: 'ceremony' }]}
|
||||
photos={[{ id: 1, category_id: 1 } as never]}
|
||||
feedbackEnabled
|
||||
currentFilter="all"
|
||||
onFilterChange={vi.fn()}
|
||||
/>
|
||||
);
|
||||
expect(screen.getAllByText('Feedback Filter')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('renders no chips when feedback is disabled', () => {
|
||||
render(<PhotoFilterBar {...baseProps} />);
|
||||
expect(screen.queryByText('Feedback Filter')).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user