Batch 3: FilterPanel molecule + integration across 3 steps (D-C, D-F)

New molecule:
- FilterPanel: Popover-based reusable filter trigger with active
  count badge, Clear all, Done actions. D-C: Popover for MVP.

Step integrations:
- ProvidersStep: inline Chip filter bar → FilterPanel Popover,
  search bar + filter button side-by-side in sticky header
- VenueStep: same pattern, filter chips moved into Popover
- CoffinsStep (D-F): grid-sidebar layout → wide-form (full-width
  4-col grid), category + price selects moved into FilterPanel

WizardLayout:
- Added wide-form variant (maxWidth lg, single column) for
  card grids that benefit from full width
- wide-form included in STEPPER_VARIANTS for progress bar

Storybook:
- FilterPanel stories: Default, WithActiveFilters, SelectFilters,
  CustomLabel

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 22:24:54 +11:00
parent 1c3cdbc101
commit c5581c6e9f
8 changed files with 576 additions and 252 deletions

View File

@@ -4,6 +4,7 @@ import type { SxProps, Theme } from '@mui/material/styles';
import { WizardLayout } from '../../templates/WizardLayout';
import { ProviderCard } from '../../molecules/ProviderCard';
import { SearchBar } from '../../molecules/SearchBar';
import { FilterPanel } from '../../molecules/FilterPanel';
import { Chip } from '../../atoms/Chip';
import { Typography } from '../../atoms/Typography';
import { Button } from '../../atoms/Button';
@@ -62,6 +63,8 @@ export interface ProvidersStepProps {
filters?: ProviderFilter[];
/** Callback when a filter chip is toggled */
onFilterToggle?: (index: number) => void;
/** Callback to clear all filters */
onFilterClear?: () => void;
/** Callback for the Continue button */
onContinue: () => void;
/** Callback for the Back button */
@@ -105,6 +108,7 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
onSearch,
filters,
onFilterToggle,
onFilterClear,
onContinue,
onBack,
error,
@@ -165,32 +169,37 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
{subheading}
</Typography>
{/* Search bar */}
<Box sx={{ mb: 2 }}>
<SearchBar
value={searchQuery}
onChange={onSearchChange}
onSearch={onSearch}
placeholder="Search providers..."
size="small"
/>
</Box>
{/* Filter chips */}
{filters && filters.length > 0 && (
<Box sx={{ display: 'flex', gap: 1, mb: 2, flexWrap: 'wrap' }}>
{filters.map((filter, index) => (
<Chip
key={filter.label}
label={filter.label}
selected={filter.active}
onClick={onFilterToggle ? () => onFilterToggle(index) : undefined}
variant="outlined"
size="small"
/>
))}
{/* Search bar + filter button */}
<Box sx={{ display: 'flex', gap: 1, mb: 2, alignItems: 'flex-start' }}>
<Box sx={{ flex: 1 }}>
<SearchBar
value={searchQuery}
onChange={onSearchChange}
onSearch={onSearch}
placeholder="Search providers..."
size="small"
/>
</Box>
)}
{filters && filters.length > 0 && (
<FilterPanel
activeCount={filters.filter((f) => f.active).length}
onClear={onFilterClear}
>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{filters.map((filter, index) => (
<Chip
key={filter.label}
label={filter.label}
selected={filter.active}
onClick={onFilterToggle ? () => onFilterToggle(index) : undefined}
variant="outlined"
size="small"
/>
))}
</Box>
</FilterPanel>
)}
</Box>
{/* Results count */}
<Typography