A control for choosing several labels, without a mouse and without a script #139
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.
Blocks
Reference
Postulo/postulo#139
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
Prerequisite. There is no such control in Postulo, and the two places that want one are not
only the company form.
What exists
Industries are a
CheckboxSelectMultipleover the person's ownIndustryrows, with afree-text Other industries box beside it backed by a
<datalist>of suggestions. Tags onan application are a plain
ModelMultipleChoiceField— Django's defaultSelectMultiple, ascrolling box you ctrl-click.
Neither is a label. Nothing in
static/js/app.jsor the templates draws a chip, so this is anew control rather than a restyling of an old one.
What this asks for
One control that shows the chosen values as removable labels, offers the rest, and lets
somebody add one that does not exist yet — used by industries and by tags, not written twice.
Worth being careful about
It has to work with no JavaScript at all, because everything on these forms does today.
The honest fallback is the control that exists — checkboxes, or a multiple select — with the
labels layered on top when a script is available. That is the same rule the board's dragging
follows: an addition to the control that works everywhere, never a replacement.
A chip's remove button is a target, and this project has been caught by SC 2.5.8 twice
already — once at 19 pixels on the plugins page (#115), once on the telephone rows. A × in
the corner of a label is exactly the shape that fails: it needs 24 by 24 with room around it,
and a row of eight chips is eight adjacent targets.
Each remove button needs its own name. Eight buttons all called Remove is eight
identical announcements; it has to be Remove Software, which means a string with the label
inside it — and therefore a
blocktranslateper chip rather than one shared word.The keyboard vocabulary is the whole design. Backspace removing the last chip is the
convention and it is also a way to delete something by accident; arrow keys moving between
chips, Enter committing a typed value, Escape abandoning it. Whatever is chosen, focus has to
land somewhere sensible after a removal rather than at the top of the form.
A typed value that is new is the interesting case.
Industry.named()already createsmissing industries by slug, so the server side exists; the control has to make "this one is
new" visible before saving, or people will create Fintech, FinTech and fintech and only
find out afterwards that the slug collapsed them.
Announce the change. Adding or removing a label with a script updates nothing a screen
reader notices unless the count or the change goes into a live region.
Both themes, and axe walks these pages already. A chip is a coloured pill; the contrast
of its text against its own ground is 4.5:1, and its border against the card is 3:1.
Right to left. The × sits at the end edge, not the right; the template lint already fails
a physical direction class.
Done in
969c3034.One control, two places
partials/label_attrs.htmlcarries the wrapper — the attribute names and the words — and each form keeps the native control it already drew, markeddata-labels-existing. That is what "not written twice" turned out to mean here: the two places do not draw their native controls the same way (industries are a hand-laid row of checkboxes, tags are a multiple select), so sharing the markup would have meant making one of them worse. Sharing the wrapper shares everything that has to agree.Used by the company form, the application intake form and the application editing form.
Layered, never substituted
The checkboxes, the multiple select and the box for a name that does not exist yet are all still in the page and still submitting —
hiddenkeeps an element in the form. With the script blocked, both forms are exactly what they were.The end-to-end test that matters most says so directly: remove a chip, type a new name, save, and check what the database got. A chip is a picture of a checkbox, and a picture that disagrees with what posts is worse than no picture.
The keyboard vocabulary, decided
Backspace is the convention and it is deliberately not followed. It is a way to delete something by pressing the key you press to correct a typo, in a control whose values are somebody's own vocabulary — that is a bad trade, and every chip has a remove button two keys away. There is a test that says so, with the reason in it.
Focus after a removal stays where it can be used: the chips redraw in place and the remove buttons keep their order.
The new-name case
Industry.named()already existed;Tagnow has the same, matched by slug, so Remote, remote and REMOTE stay one tag. Tags gained the "add one that does not exist yet" half that industries always had — the application form takesnew_tags, both save paths use it, and a tag made that way carries no colour, because colours are chosen on the tags page where there is room to see them beside each other.A new name is visible as new before saving — outlined rather than filled, and labelled — new. That is the specific thing you warned about: otherwise people create Fintech, FinTech and fintech and only find out afterwards that the slug collapsed them.
Everything else you flagged
h-6 w-6) in agap-2row. A test reads the stylesheet, because SC 2.5.8 has caught this project twice.{label}templates in data attributes rather than one shared word.role="status"region: Software added, Research removed.ps-3 pe-0.5, neverpl/pr, and a test reads the stylesheet for that; the arrow keys flip withdirection.Also
tests/test_labels.py(14) andtests/e2e/test_labels.py(11). Suite 4406 passed, 29 skipped; browser suite 65 passed.