Add display filter dropdowns to event list/calendar [#251]#274
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #251
Summary
[mayo_event_list](both list and calendar views) lets visitors narrow events by event type, service body, category, and tag.GET /event-manager/v1/events/facetsendpoint that walks the full (unpaginated) result set, including events from external feeds, so service bodies surfaced by external sources appear in the dropdown automatically.[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.[mayo_event_list]do not render the filter bar (compact context).mayo-events-managertext domain on every new string (PHP + JS).1.9.0inreadme.txtperCLAUDE.md.Files changed
includes/Rest/EventsController.php— register/events/facetsroute + handler.assets/js/src/components/public/EventFilters.js— new filter bar component.assets/js/src/components/public/EventList.js— fetch facets, manageactiveFiltersstate, render<EventFilters>above list/calendar, thread selections throughfetchEvents/fetchCalendarEvents.assets/css/public.css— styling for.mayo-event-filtersrow.readme.txt— changelog entry under1.9.0.Test plan
npm run buildsucceeds (verified locally).composer lintandcomposer lint:esc-urlpass (verified locally).[mayo_event_list]— filter bar appears with populated dropdowns for the facets present in the data.[mayo_event_list event_type="Service"]— event-type dropdown is hidden while the others remain.[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).🤖 Generated with Claude Code