Skip to content

Add display filter dropdowns to event list/calendar [#251]#274

Merged
dgershman merged 8 commits into
mainfrom
feature/mayo-251-add-display-filters
May 17, 2026
Merged

Add display filter dropdowns to event list/calendar [#251]#274
dgershman merged 8 commits into
mainfrom
feature/mayo-251-add-display-filters

Conversation

@dgershman

Copy link
Copy Markdown
Contributor

Closes #251

Summary

  • New filter bar above [mayo_event_list] (both list and calendar views) lets visitors narrow events by event type, service body, category, and tag.
  • Options are data-driven via a new GET /event-manager/v1/events/facets endpoint that walks the full (unpaginated) result set, including events from external feeds, so service bodies surfaced by external sources appear in the dropdown automatically.
  • Shortcode-pinned filters (e.g. [mayo_event_list event_type="Service"]) stay locked — the corresponding dropdown is hidden so visitors can't widen scope past what the site admin allowed.
  • A facet whose value list is empty is also hidden, so instances that don't use categories/tags don't see useless empty dropdowns.
  • Widget renderings of [mayo_event_list] do not render the filter bar (compact context).
  • Honors mayo-events-manager text domain on every new string (PHP + JS).
  • Adds a changelog entry under the current unreleased version 1.9.0 in readme.txt per CLAUDE.md.

Files changed

  • includes/Rest/EventsController.php — register /events/facets route + handler.
  • assets/js/src/components/public/EventFilters.jsnew filter bar component.
  • assets/js/src/components/public/EventList.js — fetch facets, manage activeFilters state, render <EventFilters> above list/calendar, thread selections through fetchEvents/fetchCalendarEvents.
  • assets/css/public.css — styling for .mayo-event-filters row.
  • readme.txt — changelog entry under 1.9.0.

Test plan

  • npm run build succeeds (verified locally).
  • composer lint and composer lint:esc-url pass (verified locally).
  • Load a page with [mayo_event_list] — filter bar appears with populated dropdowns for the facets present in the data.
  • Pick a value in each dropdown — list refreshes to the narrowed set; URL is unchanged (filter is in-memory state).
  • Switch from list to calendar view — selection is preserved and the calendar re-fetches with the same filters.
  • Add [mayo_event_list event_type="Service"] — event-type dropdown is hidden while the others remain.
  • Configure an external source under Settings → External Sources — service bodies from that source appear in the dropdown with the correct names.
  • Add [mayo_event_list source_ids="external-1"] — facet options reflect only that external source's events.
  • [mayo_event_list] inside a Text widget — no filter bar (widget mode skipped).
  • "Clear filters" button resets all dropdowns and reloads the unfiltered list.

🤖 Generated with Claude Code

dgershman and others added 8 commits May 17, 2026 13:31
Visitors can now narrow events by event type, service body, category, and
tag from a filter bar above the list and calendar views. Options are
populated from a new GET /events/facets endpoint that walks the full
result set (local + external feeds) so service bodies surfaced by
external sources are included automatically. Shortcode-pinned filters
(e.g. event_type="Service") remain locked and stay hidden from the
visitor-facing dropdowns.

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 92ED2F37-7067-41C3-A65F-4AA559BDD6D1
The previous dropdown filter wiring relied on threading filter overrides
through fetchEvents() and fetchCalendarEvents(). The infinite-scroll
observer and view-toggle effects called the fetchers without overrides,
so as soon as the user scrolled past the first page their selection was
silently dropped and the list looked unfiltered.

Reworked so activeFilters is the single source of truth: a useEffect
keyed on activeFilters drives refetches, URL building moves to
URLSearchParams (no more raw string concatenation), and the UI is now a
multi-select pill chip per facet. Selections within a facet are OR'd
(comma-joined to the existing REST endpoint); selections across facets
are AND'd.

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 92ED2F37-7067-41C3-A65F-4AA559BDD6D1
The pill grid was visually noisy and, more importantly, the filter
plumbing still had a subtle bug: an "isInitialFilterEffect" ref tried to
skip the first activeFilters effect on mount but raced with the init
useEffect's own fetchEvents call, occasionally landing on a state where
the URL was built before activeFilters reflected the user's chip click.

Reworked so there's a single source of truth: one useEffect keyed on
activeFilters drives every refetch (initial mount included). The init
effect now only resets the local UI state and kicks off the facets
request — it no longer calls fetchEvents directly.

Filter UI is now a compact dropdown per facet with selected values
rendered below as ×-removable chips. Saves space when no filters are
applied; supports multi-select with OR semantics inside a facet and AND
across facets, same as before.

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 92ED2F37-7067-41C3-A65F-4AA559BDD6D1
Root cause of "filtering still shows other types": fetch_all_external_events()
and fetch_external_events() only forwarded the admin-pinned source filters
from mayo_external_sources — they never passed the visitor's runtime
$_GET['event_type'] / $_GET['service_body'] / $_GET['categories'] /
$_GET['tags'] selections through to the remote feed. So local events
filtered correctly but every event from an external Mayo feed leaked
back into the merged result regardless of the chip selection.

Added a build_external_filter_params() helper that intersects each
admin pin with the visitor's selection (so an admin restriction can't
be widened, but the visitor can narrow within it) and routed both
external-fetch call sites through it.

Also compacted the filter bar UI per request: removed the per-facet
text labels (the dropdown placeholder names the facet), and laid the
bar out as a single horizontal flex row that wraps only when content
overflows.

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 92ED2F37-7067-41C3-A65F-4AA559BDD6D1
Dropdown placeholders are now the proper-cased facet name only — Event
Type, Service Body, Category, Tag — instead of "Filter by event type…"
etc. Each label is registered with a translators comment and wrapped in
__() with the mayo-events-manager text domain, and the placeholder text
is also used as the dropdown's title and aria-label.

Replaced the "Clear filters" text button with a 🧹 emoji that has a
tooltip and aria-label (both translatable). Regenerated the POT so
translators get the new strings.

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 92ED2F37-7067-41C3-A65F-4AA559BDD6D1
🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 92ED2F37-7067-41C3-A65F-4AA559BDD6D1
🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 92ED2F37-7067-41C3-A65F-4AA559BDD6D1
Replaces the always-open dropdowns with a row of compact uppercase
pills (one per facet). Clicking a pill opens a popover panel anchored
below it, with the facet title and an × close button. Inside the
panel, each option is a toggleable row; selected options are
highlighted and the pill itself shows a count badge.

This frees up the original real estate occupied by four standing
dropdowns — when nothing is selected the bar is just four short pills,
and selections collapse into a count instead of inline chips.

Keyboard/accessibility:
- pills have aria-haspopup/aria-expanded
- panel has role="listbox" with aria-multiselectable
- options have role="option" and aria-selected
- Esc and outside-click close the panel
- close button has translatable per-facet aria-label

🐦‍⬛ Generated with Claude Code, orchestrated by Crow

Co-Authored-By: Claude <noreply@anthropic.com>
Crow-Session: 92ED2F37-7067-41C3-A65F-4AA559BDD6D1
@dgershman dgershman merged commit 2e1f9d1 into main May 17, 2026
3 checks passed
@dgershman dgershman deleted the feature/mayo-251-add-display-filters branch May 17, 2026 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Filters to display options

1 participant