The "Name, if Other" box shows on every row, including the kinds that already have a name #284

Closed
opened 2026-09-19 10:05:54 +00:00 by tiagoagueda · 0 comments
Owner

A row that names a known kind — VAT number, ORCID, Mobile — still draws an empty box labelled “Name, if Other” beside it. The box is meaningless for every kind but one, it is the widest thing competing with the value for space, and it invites somebody to type in it. It should appear when the kind is Other, and not otherwise.

The rule is not new: the display side already follows it. CompanyIdentifier.scheme_label (jobs/models.py:432) and PersonIdentifier.display_label (accounts/models.py:225) both use the label only when the scheme is other, and fall back to the registry's name for every other scheme. The form is the one place that contradicts what the rest of the code already knows.

Where it is

Three row types, each a kind/scheme select with an Other-only name box drawn unconditionally beside it:

Where Line Rows affected
Company identifiers templates/jobs/company_form.html:79-82 7 of the 8 schemes a company can carry
Person identifiers templates/accounts/profile.html:152-155 6 of the 7 a person can carry
Phone numbers templates/partials/phone_numbers.html:43-44 every kind but Other

The registry has 11 schemes (plugins/identifiers/schemes.py), of which other is one — the only one whose uniqueness constraint is deliberately relaxed, because it is “a labelled free slot” (jobs/models.py:359). The model field says the same in its own help text: “What the identifier is, when the scheme is Other.”

The two identifier blocks are byte-identical — same grid, same three columns, same delete control — duplicated across two templates. #263 made a component layer for exactly this; if the markup is being touched anyway, it should become one <c-…> rather than two copies that can drift.

Web links are not part of this. partials/web_links.html:32 renders the same model field but the template relabels it “Name”, and the block already fixes the kind, so the field is meaningful on every row there. Worth noting only because the underlying field's verbose_name is name, if Other (core/models.py:404) and reads wrongly for that use — a one-word correction, not a behaviour change.

The defect underneath it

Nothing clears label when the scheme stops being Other. Neither CompanyIdentifierForm.clean (jobs/forms.py:289) nor PersonIdentifierForm.clean (accounts/forms.py:528) touches it: they check that a scheme was chosen and a value typed, and that is all. So typing a name, then changing the kind to VAT number, stores the name — where scheme_label then ignores it. The value is invisible everywhere and still in the database and the export.

That is the half worth fixing regardless of what the form draws: when the scheme is not Other, the label should be blanked on save.

How to hide it without a script

Postulo does not ship controls that need JavaScript to work, so the obvious change handler is not the answer on its own. Two pieces, and the second is already an established technique here:

  1. Server-side for the state it loads in. The row knows its scheme when it renders; mark the name column with an attribute when the scheme is other.
  2. CSS :has() for changes made on the page, so switching the select reveals the box with nothing loaded. app.css:823 and :833 already use :has(), so this is not a new dependency. The shape is a row that hides the column unless the select's other option is :checked — which updates live as the selection changes, with no script at all.

One rule this must not break: never hide a box that has something in it. If a row somehow carries a label with a non-Other scheme — every row written before this lands can — hiding the field makes a stored value invisible and unremovable. The template should keep the column when row.label.value is non-empty, whatever the scheme. Combined with the blank-on-save above, such rows clean themselves up the next time they are saved.

Points to settle

  • What happens to the column's space. The grid is a fixed four-track template — sm:grid-cols-[minmax(0,12rem)_minmax(0,1fr)_minmax(0,10rem)_auto] — so rows with the box and rows without it will not line up unless this is decided. Either keep the track and leave it empty (rows stay aligned, 10rem of nothing on most rows), or let Identifier take the space and accept that a row grows when it becomes Other. The second reads better and is the reason to do this at all; it should be looked at with several rows on screen before choosing.
  • Whether the label is required when the scheme is Other. Today it is not: an Other identifier with no name falls back to the word “Other” in scheme_label, which tells nobody anything. Requiring it once the field is the only one showing is cheap and probably right — but it is a new validation error on an existing form, so say so deliberately.
  • Phone numbers use the same field but a different form. Confirm the same clean-on-save rule is applied there and not only to the two identifier formsets.

Notes for whoever takes it

  • No migration: nothing about the columns changes. A data migration to blank the stray labels is optional — the save rule fixes them as rows are touched — and is probably not worth one.
  • tests/e2e/test_reflow.py (320 pixels) and test_target_size.py will both notice a changed grid; the profile page is one of the pages #167 found running off a narrow screen, so check it there.
  • The browser suite reads the live tree, so no template or stylesheet edit while it runs; npm run build:css if the templates gain classes, since the compiled CSS is committed and CI checks it.
  • If the shared row becomes a cotton component, tests/test_page_coverage.py is unaffected but the two pages both want a browser check, because they are the same markup in two layouts.
  • Any new or reworded string goes into fr-fr, pt-pt and pt-br as draft; the other 36 at the release sweep.
A row that names a known kind — *VAT number*, *ORCID*, *Mobile* — still draws an empty box labelled **“Name, if Other”** beside it. The box is meaningless for every kind but one, it is the widest thing competing with the value for space, and it invites somebody to type in it. It should appear when the kind is *Other*, and not otherwise. The rule is not new: the **display** side already follows it. `CompanyIdentifier.scheme_label` (`jobs/models.py:432`) and `PersonIdentifier.display_label` (`accounts/models.py:225`) both use the label *only* when the scheme is `other`, and fall back to the registry's name for every other scheme. The form is the one place that contradicts what the rest of the code already knows. ## Where it is Three row types, each a `kind`/`scheme` select with an Other-only name box drawn unconditionally beside it: | Where | Line | Rows affected | |---|---|---| | Company identifiers | `templates/jobs/company_form.html:79-82` | 7 of the 8 schemes a company can carry | | Person identifiers | `templates/accounts/profile.html:152-155` | 6 of the 7 a person can carry | | Phone numbers | `templates/partials/phone_numbers.html:43-44` | every kind but *Other* | The registry has 11 schemes (`plugins/identifiers/schemes.py`), of which `other` is one — the only one whose uniqueness constraint is deliberately relaxed, because it is *“a labelled free slot”* (`jobs/models.py:359`). The model field says the same in its own help text: *“What the identifier is, when the scheme is Other.”* **The two identifier blocks are byte-identical** — same grid, same three columns, same delete control — duplicated across two templates. #263 made a component layer for exactly this; if the markup is being touched anyway, it should become one `<c-…>` rather than two copies that can drift. **Web links are not part of this.** `partials/web_links.html:32` renders the same model field but the template relabels it *“Name”*, and the block already fixes the kind, so the field is meaningful on every row there. Worth noting only because the underlying field's `verbose_name` is `name, if Other` (`core/models.py:404`) and reads wrongly for that use — a one-word correction, not a behaviour change. ## The defect underneath it **Nothing clears `label` when the scheme stops being Other.** Neither `CompanyIdentifierForm.clean` (`jobs/forms.py:289`) nor `PersonIdentifierForm.clean` (`accounts/forms.py:528`) touches it: they check that a scheme was chosen and a value typed, and that is all. So typing a name, then changing the kind to *VAT number*, stores the name — where `scheme_label` then ignores it. The value is invisible everywhere and still in the database and the export. That is the half worth fixing regardless of what the form draws: **when the scheme is not Other, the label should be blanked on save.** ## How to hide it without a script Postulo does not ship controls that need JavaScript to work, so the obvious `change` handler is not the answer on its own. Two pieces, and the second is already an established technique here: 1. **Server-side for the state it loads in.** The row knows its scheme when it renders; mark the name column with an attribute when the scheme is `other`. 2. **CSS `:has()` for changes made on the page**, so switching the select reveals the box with nothing loaded. `app.css:823` and `:833` already use `:has()`, so this is not a new dependency. The shape is a row that hides the column unless the select's `other` option is `:checked` — which updates live as the selection changes, with no script at all. **One rule this must not break: never hide a box that has something in it.** If a row somehow carries a label with a non-Other scheme — every row written before this lands can — hiding the field makes a stored value invisible and unremovable. The template should keep the column when `row.label.value` is non-empty, whatever the scheme. Combined with the blank-on-save above, such rows clean themselves up the next time they are saved. ## Points to settle - **What happens to the column's space.** The grid is a fixed four-track template — `sm:grid-cols-[minmax(0,12rem)_minmax(0,1fr)_minmax(0,10rem)_auto]` — so rows with the box and rows without it will not line up unless this is decided. Either keep the track and leave it empty (rows stay aligned, 10rem of nothing on most rows), or let *Identifier* take the space and accept that a row grows when it becomes Other. The second reads better and is the reason to do this at all; it should be looked at with several rows on screen before choosing. - **Whether the label is *required* when the scheme is Other.** Today it is not: an Other identifier with no name falls back to the word “Other” in `scheme_label`, which tells nobody anything. Requiring it once the field is the only one showing is cheap and probably right — but it is a new validation error on an existing form, so say so deliberately. - **Phone numbers use the same field but a different form.** Confirm the same clean-on-save rule is applied there and not only to the two identifier formsets. ## Notes for whoever takes it - No migration: nothing about the columns changes. A **data migration to blank the stray labels** is optional — the save rule fixes them as rows are touched — and is probably not worth one. - `tests/e2e/test_reflow.py` (320 pixels) and `test_target_size.py` will both notice a changed grid; the profile page is one of the pages #167 found running off a narrow screen, so check it there. - The browser suite reads the live tree, so no template or stylesheet edit while it runs; `npm run build:css` if the templates gain classes, since the compiled CSS is committed and CI checks it. - If the shared row becomes a cotton component, `tests/test_page_coverage.py` is unaffected but the two pages both want a browser check, because they are the same markup in two layouts. - Any new or reworded string goes into `fr-fr`, `pt-pt` and `pt-br` as `draft`; the other 36 at the release sweep.
tiagoagueda added this to the 0.4.0 milestone 2026-09-19 10:05:54 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#284
No description provided.