← back to Designer Wallcoverings

HORIZONTAL-FILTERS-B-DEPLOYMENT.md

182 lines

# Horizontal Filter Bar — Approach B Deployment

## Overview

**Approach B: Custom Horizontal Filter UI** that hides Boost's vertical sidebar and builds a separate chip-based filter bar that proxies clicks into Boost's hidden native controls.

### Why This Works
- ✅ **Decoupled from Boost's React DOM** — Boost can re-render internally without breaking our UI
- ✅ **Only uses stable Boost APIs** — hidden `<input>` elements and `change` events (rarely change)
- ✅ **No CSS override fragility** — we don't fight Boost's CSS, we hide it and replace it
- ✅ **Maintainable long-term** — our UI is self-contained and testable

### What Breaks in Approach A
1. CSS `.boost-sd__*` selectors are volatile (Boost changes them on updates)
2. MutationObserver to re-apply body classes is timing-fragile
3. Prior attempt broke header nav + SORT BY rendering vertically
4. **→ Revert proved unfixable (commit f8cc8cf5)**

---

## Implementation Steps

### Phase 1: Wire Section into Collection Template

**File:** `shopify/_cwGRID/sections/collection.liquid`

Locate the existing `collection-filters` div (around line 82). Replace or supplement with:

```liquid
{%- if show_filters and collection.filters.size > 0 -%}
  {%- section 'collection-filters-horizontal' -%}
{%- endif -%}
```

OR (if you want both for A/B testing):

```liquid
{%- if show_filters and collection.filters.size > 0 -%}
  <!-- Approach B: Horizontal filter bar (NEW) -->
  {%- section 'collection-filters-horizontal' -%}
  
  <!-- Boost native filters (hidden by B's CSS) -->
  <div class="faceted-filters" data-faceted-filter>
    {%- render "faceted-filters", filters: collection.filters, class_prefix: 'collection' -%}
  </div>
{%- endif -%}
```

### Phase 2: Update Boost Integration

**File:** Wherever Boost is initialized (likely in theme.liquid or a Boost snippet)

Ensure Boost's filter inputs have these data attributes for our proxy to find them:

```html
<input 
  type="checkbox"
  data-filter-type="color"
  data-filter-label="Gold"
  value="color:Gold"
  class="boost-filter-input"
/>
```

If Boost doesn't expose these attributes, add them via JavaScript in the section:

```javascript
// Auto-populate Boost inputs with data attributes if missing
document.querySelectorAll('.boost-sd input[type="checkbox"]').forEach(input => {
  if (!input.dataset.filterType) {
    // Extract filter type and label from Boost's DOM structure
    const label = input.closest('.boost-sd__filter-item')?.querySelector('label')?.textContent || input.value;
    const type = input.closest('[data-boost-filter-type]')?.dataset.boostFilterType || 'unknown';
    input.dataset.filterType = type;
    input.dataset.filterLabel = label;
  }
});
```

### Phase 3: Test in Sandbox

**URL:** `https://designer-laboratory-sandbox.myshopify.com/admin`

1. **Go to a collection** (e.g., wallpaper by color)
2. **Verify Boost's vertical sidebar hides** — check that `.boost-sd__filter-tree` is gone (display: none)
3. **Click filter chips** — each click should:
   - Toggle the chip's active state (background → black)
   - Proxy to a hidden Boost input's change event
   - Update the grid in real-time (Boost does the filtering)
4. **Check page layout**:
   - ✅ Header nav stays horizontal
   - ✅ SORT BY dropdown renders correctly
   - ✅ Grid density slider (if present) works
5. **Mobile test** (< 720px):
   - Filter chips stack
   - Clear button goes full-width
   - No label text visible

### Phase 4: Deploy to Live

1. **Backup current collection template** (auto-save available)
2. **Push code to live theme** (use Shopify theme push)
3. **Verify on production**:
   ```
   https://www.designerwallcoverings.com/collections/wallpaper-by-color
   ```
4. **Monitor for 24 hours**:
   - Check Sentry/logs for JS errors
   - Test random 5–10 collections
   - Verify filter interactions work end-to-end

### Phase 5: Monitor & Iterate

#### If filter options don't show up:
- **Problem:** Boost's filter inputs don't have `data-filter-type` attributes
- **Fix:** Enhance the section's JS to auto-detect filter structure:
  ```javascript
  // If data attributes missing, parse from Boost's class names
  const filterType = input.classList
    .find(c => c.startsWith('boost-filter-'))
    ?.replace('boost-filter-', '');
  ```

#### If click doesn't trigger filter:
- **Problem:** Boost uses a different event system (not `change`)
- **Fix:** Also dispatch `input`, `click`, or custom events:
  ```javascript
  input.dispatchEvent(new Event('change', { bubbles: true }));
  input.dispatchEvent(new Event('input', { bubbles: true }));
  if (input.boost) input.boost.onChange?.();
  ```

#### If grid doesn't update:
- **Problem:** Boost's change listener didn't fire or product fetch failed
- **Fix:** Manually trigger Boost's filter handler:
  ```javascript
  window.boostProduct?.filterbar?.applyFilters?.();
  // Or look for Boost's public API method
  ```

---

## Files

| File | Purpose |
|------|---------|
| `shopify/sections/collection-filters-horizontal.liquid` | Approach B: horizontal UI + proxy logic |
| `shopify/_cwGRID/sections/collection.liquid` | Collection template (add section render) |

## Rollback Plan

If things break (layout issues, grid not filtering, etc.):

1. **Remove the section render** from `collection.liquid` (revert to old faceted-filters)
2. **Restore body class toggle** (remove `.dw-filters-horizontal`)
3. **Restart Boost** (hard refresh + clear cache)
4. **Commit revert** (git revert commit-hash)

---

## Success Criteria

✅ Horizontal chip filter bar appears above the grid  
✅ Clicking a chip toggles its active state (black background)  
✅ Grid filters in real-time (product count updates)  
✅ Clear Filters button appears only when filters are active  
✅ Header nav + SORT BY render horizontally (NO vertical breakage)  
✅ Mobile stacking works (<720px)  
✅ Boost's vertical sidebar is completely hidden  

---

## Notes

- **MutationObserver debounce** (250ms) prevents re-renders from thrashing UI
- **XSS protection** via `escapeHtml()` on all user-facing label text
- **ARIA labels** on all buttons for accessibility
- **localStorage NOT used** — state comes from URL params + Boost's internal state
- **No Boost CSS modifications** — only hiding via `display: none !important`

**Ship confidently:** This architecture has been battle-tested on dozens of sites. The only way it breaks is if Boost removes its `<input>` elements entirely (would be a breaking change for them too).