Postal addresses: show the country's flag, as the telephone field does #214

Closed
opened 2026-09-15 19:44:26 +00:00 by tiagoagueda · 0 comments
Owner

The Country chooser in a postal address row is a plain list of names with no flag, while the country chooser beside every telephone number shows the chosen country's flag (#53, #88). Addresses should show it too, the same way.

What it is today

src/postulo/templates/partials/postal_addresses.html:52-55, on Your details and the contact form while Several postal addresses is on:

<label class="field-label" for="{{ row.country.id_for_label }}">{% translate "Country" %}</label>
{{ row.country|add_class:"field-input" }}

It is a plain Django ChoiceField (core/postal.py:169-175). Its choices come from phones.country_choices(), which lists country names with no dialling codes, so the list is already right for an address; it only lacks the flag.

What already exists to reuse

The telephone field solved exactly this, and the address chooser uses the same list of countries:

  • Markup: partials/phone_widget.html:21-34.
    • The flag sits over the closed select, in a space reserved whether or not one is showing, so choosing a country moves nothing.
    • Each <option> carries its own flag's URL in data-flag="{% flag_url option.value %}", because static files are served under a content hash and a script cannot build the URL itself.
    • The select has ps-9 to make room for the flag, and min-w-0, so it can shrink.
  • Script: static/js/app.js:23-57 follows the select on change, delegated from the document. It is keyed on [data-phone-country] and finds the flag holder through [data-phone-flag].
  • Server side: the {% flag %} tag draws the right flag for the value the row loaded with, so the field is correct with scripts off; it only stops following the select until the form is saved.
  • Artwork: tests/test_flags.py::test_the_list_holds_every_country_the_telephone_field_offers already guarantees a flag for every country in phones.COUNTRIES, the same list the address chooser offers, so no new artwork is needed.

What a fix has to settle

  • Share rather than copy.
    • Pull the flag-over-select markup into a partial both fields include, e.g. partials/country_select.html.
    • Generalise the script's attributes, e.g. data-country-select / data-country-flag, instead of teaching it a second pair of names.
    • Keep the phone field's behaviour identical: tests/e2e/test_phone_flag.py pins it.
  • Decorative, as everywhere else: alt="" and aria-hidden="true". The country name in the select already says which country it is.
  • Width: the country sits in a two-column grid beside Region (sm:grid-cols-2, :47), so it is wider than the phone field's fixed w-40 and the flag has room. Check it at phone width, where the columns stack.
  • Labels that depend on the country: the region, postcode and municipality labels follow the country the row loaded with (row.labels_for_country, #147), not the one just chosen. That does not change here, but the flag makes it more obvious that the country was changed and the labels were not. Note it, and file separately if it should follow live.
  • Physical sides: start-2.5 and ps-9, as the phone widget uses; tests/test_template_lint.py fails on left/pl.
  • Shown addresses: showing flags where addresses are displayed (a contact's page, the CV header, which shows town and country) is a separate question. This issue is the chooser on the form. Rendered documents and print should not grow flags either way.

Checks

  • Tests:
    • an address row renders data-flag on every country option and draws the flag for the country it loaded with;
    • a row with no country draws none;
    • the phone field's existing flag tests still pass unchanged.
  • Browser suite: tests/e2e/test_accessibility.py visits Your details and the contact form; axe stays clean in both themes and right to left. Extend test_phone_flag.py (or add a sibling) to change an address's country and see the flag follow.
  • CSS: npm run build:css if new classes appear.
The *Country* chooser in a postal address row is a plain list of names with no flag, while the country chooser beside every telephone number shows the chosen country's flag (#53, #88). Addresses should show it too, the same way. ## What it is today `src/postulo/templates/partials/postal_addresses.html:52-55`, on *Your details* and the contact form while *Several postal addresses* is on: ```django <label class="field-label" for="{{ row.country.id_for_label }}">{% translate "Country" %}</label> {{ row.country|add_class:"field-input" }} ``` It is a plain Django `ChoiceField` (`core/postal.py:169-175`). Its choices come from `phones.country_choices()`, which lists country names with no dialling codes, so the list is already right for an address; it only lacks the flag. ## What already exists to reuse The telephone field solved exactly this, and the address chooser uses the same list of countries: - **Markup:** `partials/phone_widget.html:21-34`. - The flag sits over the closed select, in a space reserved whether or not one is showing, so choosing a country moves nothing. - Each `<option>` carries its own flag's URL in `data-flag="{% flag_url option.value %}"`, because static files are served under a content hash and a script cannot build the URL itself. - The select has `ps-9` to make room for the flag, and `min-w-0`, so it can shrink. - **Script:** `static/js/app.js:23-57` follows the select on `change`, delegated from the document. It is keyed on `[data-phone-country]` and finds the flag holder through `[data-phone-flag]`. - **Server side:** the `{% flag %}` tag draws the right flag for the value the row loaded with, so the field is correct with scripts off; it only stops following the select until the form is saved. - **Artwork:** `tests/test_flags.py::test_the_list_holds_every_country_the_telephone_field_offers` already guarantees a flag for every country in `phones.COUNTRIES`, the same list the address chooser offers, so no new artwork is needed. ## What a fix has to settle - **Share rather than copy.** - Pull the flag-over-select markup into a partial both fields include, e.g. `partials/country_select.html`. - Generalise the script's attributes, e.g. `data-country-select` / `data-country-flag`, instead of teaching it a second pair of names. - Keep the phone field's behaviour identical: `tests/e2e/test_phone_flag.py` pins it. - **Decorative, as everywhere else:** `alt=""` and `aria-hidden="true"`. The country name in the select already says which country it is. - **Width:** the country sits in a two-column grid beside *Region* (`sm:grid-cols-2`, `:47`), so it is wider than the phone field's fixed `w-40` and the flag has room. Check it at phone width, where the columns stack. - **Labels that depend on the country:** the region, postcode and municipality labels follow the country the row *loaded* with (`row.labels_for_country`, #147), not the one just chosen. That does not change here, but the flag makes it more obvious that the country was changed and the labels were not. Note it, and file separately if it should follow live. - **Physical sides:** `start-2.5` and `ps-9`, as the phone widget uses; `tests/test_template_lint.py` fails on `left`/`pl`. - **Shown addresses:** showing flags where addresses are *displayed* (a contact's page, the CV header, which shows town and country) is a separate question. This issue is the chooser on the form. Rendered documents and print should not grow flags either way. ## Checks - **Tests:** - an address row renders `data-flag` on every country option and draws the flag for the country it loaded with; - a row with no country draws none; - the phone field's existing flag tests still pass unchanged. - **Browser suite:** `tests/e2e/test_accessibility.py` visits *Your details* and the contact form; axe stays clean in both themes and right to left. Extend `test_phone_flag.py` (or add a sibling) to change an address's country and see the flag follow. - **CSS:** `npm run build:css` if new classes appear.
tiagoagueda added this to the 0.4.0 milestone 2026-09-15 21:33:21 +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#214
No description provided.