Enhance UX: Independent Scroll For SheetFinder Panels

by Admin 54 views
Enhancing SheetFinder UX: Independent Scroll for Filter, Search, and Chat Panels

Hey guys, let's dive into how we can seriously level up the user experience in SheetFinder! Currently, the app has a single, page-level scroll, which isn't ideal. Imagine scrolling through a mountain of product results and losing your chat context or having the filter panel vanish just when you need it. Not cool, right? So, let's break down the problem, propose a solution, and get technical on how to make it happen.

Overview

Currently, SheetFinder operates with a single page-level scroll, causing usability issues. This means that as users scroll through search results, they lose the context of their chat, the filter panel disappears during product browsing, and referencing chat history while interacting with filters or products becomes impossible. This is a problem we can solve!

Our Goal:

To implement independent vertical scrolling for the Filter Panel (left), the Product Grid (center), and a fixed position for the Chat Panel (right), with its own internal scrolling for message history. This way, everything stays in its place, making for a much smoother experience.

The Problem: One Scroll to Rule Them All

Current Behavior

Right now, the entire page scrolls as one big unit thanks to the MainLayout. When you scroll down to check out more products, the filter panel and chat panel vanish off-screen. This breaks the flow of conversation and forces users to scroll all the way back up to tweak filters or continue chatting. Annoying, isn't it?

User Impact

  • Lost Context: Chat history disappears while browsing products.
  • Inefficient Filtering: The filter panel vanishes, making it a pain to adjust filters on the fly.
  • Poor Chat UX: Maintaining a conversation while exploring search results becomes clunky and frustrating.

Why This Matters

The AI-powered chat assistant is a core feature of SheetFinder. Users need to have constant access to it so they can:

  1. Ask follow-up questions while checking out products.
  2. Fine-tune their searches based on what they're seeing.
  3. Refer back to previous chat responses when making decisions.

If we don't address this, we're essentially hobbling one of SheetFinder's key strengths. Time to fix it!

The Solution: Three Scroll Containers to the Rescue

Our mission is to implement three independent scroll containers, each with its own overflow superpowers.

1. Filter Panel (Left Column)

  • Add overflow-y: auto to the filter panel container. This allows vertical scrolling within the panel.
  • Set a max-height based on the viewport height, minus the header height. This ensures the panel doesn't overflow the screen.
  • Keep the current width at 280px (using LAYOUT.filterPanelWidth).

2. Product Grid (Center Column)

  • Slap an overflow-y: auto on the product grid container. Vertical scrolling, here we come!
  • Set the max-height to match the filter panel's height. Consistency is key.
  • Allow the horizontal width to flex between the filter and chat panels. Let's keep things responsive.

3. Chat Panel (Right Column)

  • Use position: fixed or position: sticky for desktop view. This will keep the chat panel glued to the screen while scrolling.
  • The chat already has internal scrolling for messages (ChatPanel.tsx line 134). Awesome!
  • Make sure the entire panel stays in the viewport, no matter how much the user scrolls.

Technical Deep Dive

Component Architecture

We're using MUI (Material-UI) with a slick design token system. Here are the key players:

Main Layout Structure:

  • src/components/layout/MainLayout.tsx: The main stage, featuring the three-column layout (FilterPanel | ProductGrid | ChatPanel).
  • src/components/filters/FilterPanel.tsx: The left sidebar, rocking those accordion filters.
  • src/components/products/ProductGrid.tsx: The center stage for our product results.
  • src/components/chat/ChatPanel.tsx: The right-side chat interface (for desktop).
  • src/components/chat/ChatDrawer.tsx: The chat drawer for mobile users.

Design System:

  • Design tokens live in src/theme/tokens/. This ensures consistency across the app.
  • Spacing follows an 8px grid system (spacing.ts). Neat and tidy.
  • Z-index is defined in zIndex.ts (sticky: 1100, fixed: 1200). Keep those layers in order.
  • Layout constants in MainLayout.tsx use design tokens. No magic numbers here!

Existing Scroll Patterns

Here's a glimpse of scroll implementations already in the codebase:

// ChatPanel.tsx - Already has internal message scroll
<Box
  sx={{
    flex: 1,
    overflowY: 'auto',
    p: CHAT_SPACING.messages,
    // ... message container
  }}
>

// ProductCarousel.tsx - Horizontal scroll example
<Box
  sx={{
    display: 'flex',
    overflowX: 'auto',
    scrollBehavior: 'smooth',
    '&::-webkit-scrollbar': { height: 8 },
    '&::-webkit-scrollbar-thumb': {
      backgroundColor: 'divider',
      borderRadius: 4
    }
  }}
>

Responsive Behavior

Let's make sure this looks great on all devices!

Mobile (xs-sm breakpoints):

  • Filter panel is hidden (already done).
  • Product grid takes up the full width and scrolls naturally.
  • Chat opens as a full-screen drawer (already implemented in ChatDrawer.tsx).

Tablet (md breakpoint):

  • Filter panel appears (280px width).
  • Product grid adjusts its width.
  • Chat opens as a fixed sidebar.

Desktop (md+ breakpoint):

  • All three columns are visible.
  • Each has independent scrolling.
  • The chat panel is resizable (ResizeHandle is already in place).

Accessibility Considerations

Let's make sure everyone can use this!

  1. ARIA Landmarks:
    • Filter panel: role="complementary" or role="navigation"
    • Product grid: role="main"
    • Chat panel: role="complementary" (already implemented)
  2. Keyboard Navigation:
    • Ensure tab order flows logically through scroll containers.
    • Each scroll container should be keyboard-accessible.
    • Test with screen readers (NVDA, JAWS).
  3. Focus Management:
    • Focus should remain visible when scrolling programmatically.
    • Implement focus trapping within scroll containers when needed.
  4. WCAG 2.1 Compliance:
    • Maintain contrast ratios for scrollbar styling.
    • Provide sufficient touch targets (44x44px minimum).
    • Ensure scrollable regions are identifiable.

Performance Considerations

We want this to be smooth and responsive!

  1. Scroll Event Optimization:
    • Use passive event listeners where possible.
    • Consider IntersectionObserver for scroll-based features.
    • Debounce/throttle scroll handlers if needed.
  2. Virtual Scrolling:
    • Not needed initially (product grid is paginated).
    • Consider for filter panel if filter lists grow very large.
  3. Rendering Performance:
    • Test with DevTools Performance profiler.
    • Monitor for layout thrashing.
    • Aim for smooth 60fps scrolling.

Implementation Plan: Let's Get Rolling!

Phase 1: Filter Panel Independent Scroll

Files: src/components/layout/MainLayout.tsx, src/components/filters/FilterPanel.tsx

Tasks:

  • [ ] Add max-height constraint to the filter panel container based on viewport.
  • [ ] Apply overflow-y: auto to the filter panel wrapper.
  • [ ] Style the scrollbar using the existing pattern from ProductCarousel.tsx.
  • [ ] Test accordion expand/collapse with the scroll container.
  • [ ] Verify mobile behavior remains unchanged.

Example Implementation:

// MainLayout.tsx
<Box
  sx={{
    width: LAYOUT.filterPanelWidth,
    maxHeight: `calc(100vh - ${theme.mixins.toolbar.minHeight}px - ${spacing.lg}px)`,
    overflowY: 'auto',
    flexShrink: 0,
    display: { xs: 'none', md: 'block' },
    '&::-webkit-scrollbar': {
      width: 8
    },
    '&::-webkit-scrollbar-thumb': {
      backgroundColor: 'divider',
      borderRadius: 4
    }
  }}
>
  <FilterPanel filters={filters} onFilterChange={applyFilters} products={results} />
</Box>

Phase 2: Product Grid Independent Scroll

Files: src/components/layout/MainLayout.tsx, src/components/products/ProductGrid.tsx

Tasks:

  • [ ] Add max-height constraint to the product grid container.
  • [ ] Apply overflow-y: auto to the product grid wrapper.
  • [ ] Style the scrollbar consistently with the filter panel.
  • [ ] Test loading states within the scroll container.
  • [ ] Test error states within the scroll container.
  • [ ] Verify responsive behavior on tablet/mobile.

Example Implementation:

// MainLayout.tsx
<Box
  sx={{
    flex: 1,
    minWidth: 0,
    maxHeight: `calc(100vh - ${theme.mixins.toolbar.minHeight}px - ${spacing.lg}px)`,
    overflowY: 'auto',
    '&::-webkit-scrollbar': {
      width: 8
    },
    '&::-webkit-scrollbar-thumb': {
      backgroundColor: 'divider',
      borderRadius: 4
    }
  }}
>
  <ProductGrid results={results} loading={loading} error={error} onRetry={retry} />
</Box>

Phase 3: Chat Panel Fixed Positioning

Files: src/components/layout/MainLayout.tsx, src/components/chat/ChatPanel.tsx

Tasks:

  • [ ] Change the chat panel container to use position: sticky or position: fixed.
  • [ ] Adjust z-index using zIndex.chatPanel from design tokens (currently 1200).
  • [ ] Update chat panel height calculation to account for the header.
  • [ ] Ensure the resize handle works with fixed positioning.
  • [ ] Test animation with fixed positioning (framer-motion AnimatePresence).
  • [ ] Verify mobile drawer behavior remains unchanged.

Example Implementation:

// MainLayout.tsx - Desktop Chat Panel
<Box
  component={motion.div}
  // ... existing animation props
  sx={{
    display: { xs: 'none', md: 'block' },
    width: chatWidth,
    position: 'sticky',
    top: `${theme.mixins.toolbar.minHeight}px`,
    maxHeight: `calc(100vh - ${theme.mixins.toolbar.minHeight}px - ${spacing.lg}px)`,
    flexShrink: 0,
    zIndex: zIndex.chatPanel // 1200
  }}
>
  <ChatPanel messages={messages} onSendMessage={sendMessage} onClose={handleChatClose} />
</Box>

Phase 4: Polish & Testing

Tasks:

  • [ ] Add smooth scroll behavior where appropriate.
  • [ ] Implement consistent scrollbar styling across all containers.
  • [ ] Test keyboard navigation across all scroll containers.
  • [ ] Test with screen readers (NVDA/JAWS).
  • [ ] Test on multiple browsers (Chrome, Firefox, Safari, Edge).
  • [ ] Test responsive behavior at all breakpoints (xs, sm, md, lg, xl).
  • [ ] Verify no layout shifts or visual glitches.
  • [ ] Update any affected unit tests in src/**/__tests__/.
  • [ ] Performance testing with Chrome DevTools.

Acceptance Criteria: How We Know We've Succeeded

Functional Requirements

  • [ ] Filter panel scrolls independently within its fixed-width column.
  • [ ] Product grid scrolls independently within the center column.
  • [ ] Chat panel remains fixed in the viewport during page scrolling.
  • [ ] Chat message area scrolls independently within the chat panel.
  • [ ] All three scroll containers maintain their scroll position when switching between them.
  • [ ] Scrollbar styling is consistent across all three containers.
  • [ ] Mobile drawer behavior remains unchanged (chat opens as a full-screen overlay).

Non-Functional Requirements

  • [ ] Scroll performance maintains 60fps on modern browsers.
  • [ ] No layout shifts or visual glitches during scrolling.
  • [ ] Keyboard navigation works correctly across all scroll containers.
  • [ ] Screen readers properly announce scrollable regions.
  • [ ] WCAG 2.1 AA compliance maintained for all scroll containers.
  • [ ] Touch targets meet minimum 44x44px requirement for mobile.

Quality Gates

  • [ ] All existing tests pass.
  • [ ] New tests added for scroll behavior if applicable.
  • [ ] Cross-browser testing completed (Chrome, Firefox, Safari, Edge).
  • [ ] Responsive testing completed at all breakpoints.
  • [ ] Accessibility testing completed with screen readers.
  • [ ] Code review approval from maintainers.
  • [ ] Design system tokens used consistently (no magic numbers).

Success Metrics: How We Measure Awesome

User Experience:

  • Users can continue chatting while scrolling through products.
  • Users can adjust filters without losing product scroll position.
  • Reduced need to scroll back to the top of the page.

Technical:

  • No scroll performance issues (<16ms per frame).
  • No accessibility regressions.
  • Consistent behavior across all supported browsers.

Dependencies & Risks: What We Need and What Could Go Wrong

Dependencies

  • MUI (Material-UI) v5+ - already in use
  • Framer Motion - already in use for chat panel animations
  • React 18+ - already in use

Risks & Mitigation

Risk 1: Layout Conflicts with Fixed Positioning

  • Impact: Chat panel may overlap with other content or behave unexpectedly.
  • Mitigation: Use position: sticky instead of fixed, test thoroughly with ResizeHandle component.

Risk 2: Mobile Drawer Regression

  • Impact: Changes to desktop layout may break mobile drawer.
  • Mitigation: Mobile uses separate ChatDrawer component, test both views independently.

Risk 3: Scroll Event Performance

  • Impact: Multiple scroll containers could cause performance issues.
  • Mitigation: Use passive event listeners, avoid scroll handlers unless necessary, monitor with DevTools.

Risk 4: Accessibility Issues

  • Impact: Screen readers may not properly announce scrollable regions.
  • Mitigation: Test with NVDA/JAWS, add proper ARIA attributes, ensure keyboard navigation.

References & Research: Learn From the Best

Internal References

Layout Components:

  • Main layout structure: src/components/layout/MainLayout.tsx:1-170
  • Filter panel: src/components/filters/FilterPanel.tsx:1-500
  • Chat panel desktop: src/components/chat/ChatPanel.tsx:1-240
  • Chat drawer mobile: src/components/chat/ChatDrawer.tsx:1-50
  • Product grid: src/components/products/ProductGrid.tsx

Design System:

  • Spacing tokens: src/theme/tokens/spacing.ts
  • Z-index system: src/theme/tokens/zIndex.ts:66-68 (chatPanel: 1200, sidebar: 1200, header: 1100)
  • Design system documentation: DESIGN_SYSTEM.md

Existing Scroll Patterns:

  • Horizontal scroll example: src/components/products/ProductCarousel.tsx:97-115
  • Chat message scroll: src/components/chat/ChatPanel.tsx:134
  • Scroll event handling: src/components/chat/ChatPanel.tsx:53-58

External References

MUI Documentation:

Accessibility:

CSS Overflow:

Best Practices:

Related Work

  • Issue #6: AI-powered search implementation (Phases 1-6 completed)
  • PR #20: Phase 6 testing suite (merged)
  • PR #19: Phase 5 detail page (merged)
  • PR #18: Phase 4 filters (merged)

Implementation Notes

Design Token Usage:

This codebase uses a systematic design token system. Always use tokens instead of magic numbers:

// ✅ GOOD
import { spacing } from '../../theme/tokens/spacing';
import { zIndex } from '../../theme/tokens/zIndex';

sx={{
  p: spacing.md / 8,  // 16px → 2 MUI units
  zIndex: zIndex.chatPanel  // 1200
}}

// ❌ BAD
sx={{
  p: 2,  // Magic number
  zIndex: 1200  // Hardcoded
}}

Scrollbar Styling Pattern:

Use the consistent scrollbar styling pattern from ProductCarousel.tsx:

'&::-webkit-scrollbar': {
  width: 8,  // or height: 8 for horizontal
},
'&::-webkit-scrollbar-track': {
  backgroundColor: 'transparent'
},
'&::-webkit-scrollbar-thumb': {
  backgroundColor: 'divider',
  borderRadius: 4
}

Responsive Testing:

Test at these breakpoints defined in MUI theme:

  • xs: 0px (mobile)
  • sm: 600px (tablet)
  • md: 900px (desktop - where the filter panel appears)
  • lg: 1200px
  • xl: 1536px

Let's get this done and make SheetFinder even more awesome!