RIDING BYTES
← All insights

August 5, 2026 · 6 min read

Listings 2.0: saved filters, label chips, and a searchable column popover

senaite.app.listing gained saved filter presets, click-to-filter label chips, removable filter chips in the toolbar, and a redesigned column config that scales to thirty columns. What changed and why it matters.

Listings 2.0: saved filters, label chips, and a searchable column popover

Every SENAITE user spends most of their day inside a listing. Samples, worksheets, batches, invoices. The listings look plain because they must: the density of information is the feature. Which makes small ergonomic wins compound quickly. This half, senaite.app.listing shipped several.

Saved filter presets

#174 adds a bookmark pill next to the search box. Set up your view (review state, column filters, sort, page size, search term), click the pill, save it as “Overdue serology” or “This week’s water samples”. Reopen the listing on Monday and the preset is one click away.

The storage key uses the listing’s listing_identifier (a server-computed value the column-config feature already used), with form_id as fallback. That matters when the same form_id is reused across contexts: your per-client “samples” listing and the global “samples” listing do not share presets by accident.

Each preset captures review_state, column_filters, sort_on, sort_order, pagesize, and filter. Rename, delete, and save happen via inline editors (no more window.prompt), each accepting Enter to confirm and Esc to cancel. When your live view diverges from the stored payload, the preset gains a “modified” tag with inline save and undo actions. Pick a default and it auto-applies on landing when no URL parameters are present.

The same PR fixed a subtler bug: column-filter dropdowns used to show every possible value for a column, ignoring whatever other filters were already active. You could pick a value and land on an empty table because two of your filters cannot coexist. The new apply_column_filters(query, exclude=None) signature narrows each dropdown by every other filter in force, so what you see is what you can pick.

Label chips, click-to-filter

Companion to senaite.core#2958, #178 renders sample labels as coloured chips under the primary column. Click a chip, the URL gains ?labels=<name>, the listing filters. Click a second chip, both filter. Click the same one again, it clears. An active-filter row above the search box shows every ?labels= value with an X to remove it.

The Reset button (the small spinning arrow) now also wipes ?labels=. Saved presets capture and re-apply label state via a navigation, so a bookmarked share-link with ?labels= does not get overridden by an auto-applied user default.

Removable filter chips in the toolbar

Column filters were only visible as a highlighted funnel icon on the header. If you set three filters and forgot two of them, you saw an empty table and had to hunt the funnels. #180 renders each active column filter as a <column>: <value> × chip in the toolbar. Click the X, that filter clears, the listing refetches, everything else stays put. It is generic, so any listing with column filters gets it for free.

Samples listing with the review-state pill row (Active, Due, Received, To be verified, Verified, Published, Stored, Past Retention, Dispatched, Disposed, Cancelled, Invalid, All, Rejected) at the top. Below, two removable filter chips read 'Client: Riding Bytes ×' and 'State: sample_received ×'. The table shows one filtered result for sample SDM-0002-P01 with Riding Bytes / Sediment / Received.
Every active filter as a removable chip. Click the × to clear one filter, everything else stays.

Column config as a searchable popover

Thirty columns in a horizontal chip row was legacy pain. #176 replaces it with a small [⊟ Configure] button that opens a structured popover: search input at the top with autofocus, Show all / Hide all bulk actions, a Visible section (drag handles on the left, eye-slash to hide on the right) and a Hidden section (alphabetised, + restores at the end of Visible).

The drop indicator is a 2 px accent line above or below the targeted row, computed from cursor Y within the row. No more guessing where a column will land. The popover renders via ReactDOM.createPortal to document.body, escapes the table-responsive overflow-x: auto, follows the trigger on scroll and resize, and closes on outside click or Esc.

Samples listing with the CONFIGURE TABLE COLUMNS popover open. Header shows 13 of 35 columns configured. Search input, Show all and Hide all buttons. A VISIBLE section lists Priority, Progress, Sample ID, Creator, Date Sampled, Client, Client ID, Lab Sample ID, Sample Type, State, Date stored, Storage, Retain Until with drag handles and hide toggles. Below, a HIDDEN section lists 22 more columns (Analyses, Batch ID, Client Order, Client Ref) with plus buttons to restore them.
Column popover, 13 of 35 columns visible. Search, drag, show all, hide all, or restore individual columns from the Hidden section.

Under the hood, the whole colour palette moved to --sf-*, --listing-*, and --listing-bs-* tokens in a single :root block. Add-ons that want a theme can override those tokens without rebuilding the bundle. A future [data-theme] selector can re-skin at runtime.

Smaller wins worth naming

#179 made KeywordIndex column filters populate their autocomplete from the actual indexed values and AND multiple selections instead of OR-ing them. If you pick “Ca” and “Mg” in an analysis-keyword filter, you now get rows that have both, not either.

Two people reviewing notes on paper next to a laptop

Bring this to your lab

Whether you are evaluating SENAITE, scaling it across sites, or wiring up instruments and monitoring, the team that wrote the platform is the team you talk to.