The tables take the shape Dispatcharr gives its channel list #136
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
#134 Selecting rows, and acting on all of them at once
Postulo/postulo
#135 Editing a cell where it sits, and where its refusal goes
Postulo/postulo
Reference
Postulo/postulo#136
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
Checked: Dispatcharr/Dispatcharr,
frontend/src/components/tables/ChannelsTable.jsx— TanStack Table with Mantine anddnd-kit, against a Django backend.
What exists, beside what they do
manualPagination: trueEditableTextCelland friendsTwo of their features are already here, which is worth saying before building anything:
per-column filtering as you type is
hx-trigger="input changed delay:300ms, search"inpartials/table/head.html, and it is server-side exactly as theirs is.One of them is already better here, and the difference is a decision, not an accident.
core/tables.pysays why:Dispatcharr keeps the query in session storage and the sort in memory: reload and the sort is
gone, and a filtered view cannot be sent to anybody. Whatever is adopted from them, this
should not be — a table narrowed to quiet applications at agencies is a thing to bookmark.
What this asks for
The parts of that table Postulo has not got: selection with bulk actions, editing in the
cell, resizable columns, and the sort that clicks back to none.
Worth being careful about
The two large ones each have a prerequisite — selection because bulk actions are the
first request that arrives as a list of ids from the client, and inline editing because a
cell has nowhere to put a validation error and the event log must not be bypassed.
The two small ones are nearly free and could go first.
Tri-state sorting is three lines in
Table.next_sort, which today cyclesascending → descending → ascending and never returns to the default order. Clicking back to
unsorted is a real gap: there is currently no way to undo a sort except editing the URL.
Resizable columns are the one place a script is unavoidable — a width is a pointer gesture
— but the storage already exists:
Profile.table_settingsholds visibility, order and pagesize per table, and a width map beside them changes no schema. Without a script the columns
size themselves, as now, which is a complete fallback.
Row reordering is theirs and should stay theirs. A channel list has an order somebody
chose; a company list has an order somebody sorted. Dragging a row in a sorted table means
either abandoning the sort or lying about it. Not worth taking.
The listings page is not a table at all.
ApplicationsTableandCompaniesTableare theonly two registered;
jobs/listing_list.htmlrenders its own rows, so none of the sorting andfiltering the suggestion opens by praising is available there. If the tables are getting
richer, the page most like a channel list — many rows, triaged in bulk, mostly discarded —
is the one that has none of it. Worth deciding whether this issue covers making listings a
table, or whether that is its own.
A table of a thousand rows is where these features earn their keep, and where Postulo has
never been tested. Dispatcharr manages "thousands of channels"; the largest Postulo table
in practice holds a few hundred applications. Bulk actions and inline editing are worth
building for the shape of the data rather than for the shape of the screenshot.
Every one of these is a page axe already walks in two themes, and three of them
(selection, inline edit, resize) introduce controls that did not exist. The browser suite
grows with them rather than after them.
Done in
452057c5. All four are here now, and two of them arrived before this commit did.#134#135Sorting, in three states
Two lines of arithmetic and one real gap closed: there was no way to undo a sort except by editing the address.
A column that is the table's default sort keeps two states. There is nothing to go back to — the table's own order is that column ascending — so a third click would change nothing, and offering it would be a control that lies about what it does.
The arrow no longer says the whole of it, because the third state takes the arrow away and an arrow that disappears announces nothing. Every header now carries the sentence: Sort by applications, highest first, Stop sorting by postings.
Widths, on the profile
You drew the line already and it decided this: sort and filters are a question and live in the URL; which columns show and how many rows a page holds are a preference and live on the profile. A width is a preference, so it went beside them — no schema change, as you predicted.
That is also the one thing from Dispatcharr deliberately not adopted, and there is a test saying so: theirs keeps the query in session storage and the sort in memory, so a reload loses the sort and a filtered view cannot be sent to anybody. A table narrowed to quiet applications at agencies is a thing to bookmark.
The handle, and two things the suite caught
A width is a pointer gesture, so this is the one place a script is unavoidable — but it is still an addition rather than a replacement. Without a script no handle exists at all and the columns size themselves exactly as before, which is a complete fallback.
It is not pointer-only. The handle is a
<button>: the arrow keys widen and narrow it (flipped for right-to-left), and Home lets the column size itself again — because a column dragged too narrow once must not be too narrow for ever. A control only a mouse can reach is a control half the people using this application cannot use.Two things went wrong and were caught rather than shipped:
test_target_size.pyfailed it under SC 2.5.8. It is 24 now, and the script adds the room for it at the same moment it adds the handle — so a page with no script does not reserve space for a control that is not there. That is the third time this criterion has caught this project, and the second time the suite caught it before anybody else could.Row reordering
Not taken, and I agree with your reasoning: a channel list has an order somebody chose; a company list has an order somebody sorted, and dragging a row in a sorted table means either abandoning the sort or lying about it.
The listings page
The decision you asked for: not in this issue. Making listings a table is a page rewrite rather than a table feature, and folding it into an issue that was already four features would have meant doing it badly to finish something else. It is filed as #160 for 0.4.0, with what makes that page different written down — the capture-and-discard workflow is not a column, bulk discard needs a reason, and a table's furniture over an empty list is worse than the page there is now.
Also
tests/test_table_shape.py(23) andtests/e2e/test_column_widths.py(7). Suite 4464 passed, 29 skipped; browser suite 78 passed.Follow-up, fixed in
a8368c7d: the width did nothing on a real deployment.It shipped as
style="width: 320px"on the header cell, and the policy Postulo serves isstyle-src 'self', which refuses a style attribute as firmly as it refuses an inlinescript. The browser dropped it, the column sized itself, and every load of a table with a
stored width also collected a console violation — behind which a genuine one would have been
invisible.
Neither existing test could have caught it, and that is the part worth recording.
tests/e2e/test_column_widths.pydrives a real browser but under the development settings,where the policy is not enforced;
tests/e2e/test_csp.pyserves the production policy butvisited no page that had a stored width. Both were true, both passed, and the feature was
broken in production. Two tests close it: the header carries no
style=at all, and a tablewith a stored width now loads under the real policy with no complaint and a column that is
measurably wider.
The fix puts the width where it belongs anyway. The script that owns the handle applies it
through the DOM — which
style-srcdoes not govern — so a width produced by a pointergesture is applied by the same script that provides the gesture, and with scripts off the
columns size themselves exactly as the fallback already promised.