Feature/listing rework - #438
Conversation
helenb
left a comment
There was a problem hiding this comment.
Hey @nicklee thanks for this - there are a few changes to look at from a front-end perspective.
I am hoping @tm-kn can review the js as I am less familiar with htmx - and also I've run out of time today. I can take another look next week if not.
One final comment: it's a bit confusing that if you choose the work listing filtered by public from the parent drop down, you get the charity branding. I am not sure how we'd avoid something like this as presumably you need to select one theme overall for the work listing. But might be something that causes confusion / comments from Lily and Lisa I think.
I would also recommend running the tbx-pr-review skill on this. It picked up a bunch of performance issues and missing tests when i tried it. It might be easier for you to run it than for me to copy and past them all here, but I can share the output if that would be helpful.
| | **Service** | `Service` snippets (non-culture) | `service` | Repeatable; excludes culture slugs (see below) | | ||
| | **Culture** | `Service` snippets (culture set) | `service` | UI-only split; same param as Service; badge counts culture selections | | ||
|
|
||
| Dropdowns with **no options on the unfiltered listing** and **no active selection** in that dimension are hidden (for example, Culture on Work when no culture-tagged work exists). Dropdown visibility is fixed at page load and does not change when facet narrowing removes options after other filters are applied. |
|
|
||
| --- | ||
|
|
||
| ## Filter dropdowns (Events) |
There was a problem hiding this comment.
For events I was unable to deselect past events once I had selected them. (Appears to be using a radio button not a checkbox)
Screen.Recording.2026-07-03.at.08.26.49.mov
|
|
||
| Without JavaScript: | ||
|
|
||
| - The filter form submits via **GET** to the listing URL. |
There was a problem hiding this comment.
The dropdowns don't appear to function without JavaScript, so it's impossible to select any filters. If we are going to support non-JS here we need to do it fully.
| @@ -0,0 +1,5 @@ | |||
| {% for option in listing_filters.culture %} | |||
| {% include "patterns/molecules/listing-filters/includes/filter-option.html" with param="service" value=option.value label=option.label selected=selected_culture %} | |||
There was a problem hiding this comment.
There only seemed to be one option here, as mentioned above, and it matches the name of the dropdown. Perhaps the whole filter should be hidden in this scenario.
| data-listing-filter-dropdown | ||
| {% if not show_dropdown %}hidden{% endif %} | ||
| > | ||
| <button |
There was a problem hiding this comment.
Is there a reason you've used buttons and a list here rather than a select dropdown? Select would have the benefit of working without JS.
| @use '../config' as *; | ||
|
|
||
| .listing-header { | ||
| grid-column: 2 / span 4; |
| } | ||
|
|
||
| .listing-filters { | ||
| grid-column: 2 / span 4; |
| min-width: 0; | ||
|
|
||
| @include media-query(medium) { | ||
| min-width: 9rem; |
There was a problem hiding this comment.
Is there a logic to this value? Might be worth an explanatory comment as otherwise feels a bit arbitrary.
| top: calc(100% + 6px); | ||
| left: 0; | ||
| width: max-content; | ||
| min-width: max(100%, 20rem); |
| } | ||
| } | ||
|
|
||
| [id='listing-panel'] { |
There was a problem hiding this comment.
Do we need this selector? Could it be done with a class instead?
|
@nicklee Could you use the mr template and tick off the relevant checks? |
| | -------------- | --------------------------------- | ------------------- | ----------------------------------------------- | | ||
| | 0 | Page title | (default) | Base URL if `?page=` present; else page default | | ||
| | 1 | `{title} filtered by {label}` | indexable | Current filtered URL | | ||
| | 2+ | `{title} filtered by {a}, {b}, …` | `noindex, nofollow` | Base listing URL | |
There was a problem hiding this comment.
I'm triple checking with Beth if this is right. I think we usually don't want to do canonical URLs for pages that will repeat content.
There was a problem hiding this comment.
| return sorted(merged.values(), key=lambda item: item["label"].lower()) | ||
|
|
||
|
|
||
| @dataclass(frozen=True) |
There was a problem hiding this comment.
I think we should pivot and use django-filter instead as it's a more established framework for building filters. That would also avoid reading user input directly from request.GET, and use Django forms for sanitising and validating the values, rather than passing them straight to database.
| @@ -0,0 +1,13 @@ | |||
| {% load wagtailcore_tags %} | |||
|
|
|||
| {# Always present so htmx OOB swaps can target these elements. #} | |||
There was a problem hiding this comment.
What's the signifance of using HTMX to swap those tags? Why is this needed?


Replaces the legacy single-select tag cloud (title-filters + ?filter=) on Work, News, and Events index pages with a shared multi-select dropdown filter UI, progressive enhancement via htmx, and a centralised listing backend.
What changed
UI / templates
Backend (tbx/core/listing/)
JavaScript (listing.js + listing-filters.js)
Tests
Documentation
Other