sky form TZ: render-readonly + drop #id_nf_tz_hint; placeholder absorbs the auto-detected hint copy — TDD
A user-typed TZ override fed through schedulePreview's `if (tz) params.set('tz', tz)` path made PySwiss compute the chart against a TZ that didn't match the lat/lon, so a partial edit (e.g. "America/New_Yo|") returned HTTP 400. Mirror the lat/lon convention: tz field gets readonly + tabindex:-1 across all three sky contexts (Dashsky sky.html, in-room PICK SKY _sky_overlay.html, My Sky applet _applet-my-sky.html). Auto-population still works because the JS writes via .value rather than via user input. The <small id="id_nf_tz_hint"> "Auto-detected from coordinates." line is removed; that copy now lives on the <input>'s placeholder so an empty TZ field self-explains. JS purges every tzHint reference (const declaration + 4 .textContent writes per file × 3 files).
SkyViewTest.test_tz_input_is_readonly_and_carries_auto_detect_placeholder pins the rendered Dashsky markup: id_nf_tz carries `readonly`, the placeholder is "auto-detected from coordinates", and `id="id_nf_tz_hint"` no longer appears anywhere. Existing MySkyTimezoneRefreshTest still passes — it asserts the field auto-fills via JS, which still works on a readonly input.
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,22 @@ class SkyViewTest(TestCase):
|
||||
self.assertContains(response, reverse("sky_preview"))
|
||||
self.assertContains(response, reverse("sky_save"))
|
||||
|
||||
def test_tz_input_is_readonly_and_carries_auto_detect_placeholder(self):
|
||||
"""Manual TZ edits throw the schedulePreview / PySwiss fetch off (the
|
||||
backend gets a stale TZ for the new lat/lon), so the field is render-
|
||||
readonly like lat/lon — auto-fills from preview, never from a typed
|
||||
override. The old <small id="id_nf_tz_hint"> is gone; its copy lives
|
||||
in the placeholder so an empty field is self-explanatory."""
|
||||
response = self.client.get(reverse("sky"))
|
||||
self.assertContains(response, 'id="id_nf_tz"')
|
||||
# readonly + tabindex:-1 mirrors the lat/lon pattern.
|
||||
self.assertRegex(
|
||||
response.content.decode(),
|
||||
r'id="id_nf_tz"[^>]*\breadonly\b',
|
||||
)
|
||||
self.assertContains(response, 'placeholder="auto-detected from coordinates"')
|
||||
self.assertNotContains(response, 'id="id_nf_tz_hint"')
|
||||
|
||||
|
||||
class SkyPreviewTest(TestCase):
|
||||
def setUp(self):
|
||||
|
||||
@@ -54,8 +54,8 @@
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_tz">Timezone</label>
|
||||
<input id="id_nf_tz" name="tz" type="text"
|
||||
placeholder="auto-detected from location">
|
||||
<small id="id_nf_tz_hint"></small>
|
||||
placeholder="auto-detected from coordinates"
|
||||
readonly tabindex="-1">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -145,7 +145,6 @@
|
||||
const latInput = document.getElementById('id_nf_lat');
|
||||
const lonInput = document.getElementById('id_nf_lon');
|
||||
const tzInput = document.getElementById('id_nf_tz');
|
||||
const tzHint = document.getElementById('id_nf_tz_hint');
|
||||
const suggestions = document.getElementById('id_nf_suggestions');
|
||||
|
||||
const PREVIEW_URL = section.dataset.previewUrl;
|
||||
@@ -185,7 +184,7 @@
|
||||
if (d.place) placeInput.value = d.place;
|
||||
if (d.lat) latInput.value = d.lat;
|
||||
if (d.lon) lonInput.value = d.lon;
|
||||
if (d.tz) { tzInput.value = d.tz; tzHint.textContent = 'Auto-detected from coordinates.'; }
|
||||
if (d.tz) { tzInput.value = d.tz; }
|
||||
if (_formReady()) schedulePreview();
|
||||
}
|
||||
|
||||
@@ -250,7 +249,6 @@
|
||||
latInput.value = parseFloat(place.lat).toFixed(4);
|
||||
lonInput.value = parseFloat(place.lon).toFixed(4);
|
||||
tzInput.value = '';
|
||||
tzHint.textContent = '';
|
||||
hideSuggestions();
|
||||
_saveForm();
|
||||
schedulePreview();
|
||||
@@ -269,7 +267,6 @@
|
||||
latInput.value = pos.coords.latitude.toFixed(4);
|
||||
lonInput.value = pos.coords.longitude.toFixed(4);
|
||||
tzInput.value = '';
|
||||
tzHint.textContent = '';
|
||||
fetch(
|
||||
`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latInput.value}&lon=${lonInput.value}`,
|
||||
{ headers: { 'User-Agent': USER_AGENT } }
|
||||
@@ -330,7 +327,6 @@
|
||||
_lastChartData = d;
|
||||
if (!tzInput.value && d.timezone) {
|
||||
tzInput.value = d.timezone;
|
||||
tzHint.textContent = 'Auto-detected from coordinates.';
|
||||
}
|
||||
setStatus('');
|
||||
confirmBtn.disabled = false;
|
||||
|
||||
@@ -64,9 +64,9 @@
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_tz">Timezone</label>
|
||||
<input id="id_nf_tz" name="tz" type="text"
|
||||
placeholder="auto-detected from location"
|
||||
placeholder="auto-detected from coordinates"
|
||||
readonly tabindex="-1"
|
||||
{% if saved_birth_tz %}value="{{ saved_birth_tz }}"{% endif %}>
|
||||
<small id="id_nf_tz_hint"></small>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -114,7 +114,6 @@
|
||||
const latInput = document.getElementById('id_nf_lat');
|
||||
const lonInput = document.getElementById('id_nf_lon');
|
||||
const tzInput = document.getElementById('id_nf_tz');
|
||||
const tzHint = document.getElementById('id_nf_tz_hint');
|
||||
const suggestions = document.getElementById('id_nf_suggestions');
|
||||
|
||||
const PREVIEW_URL = overlay.dataset.previewUrl;
|
||||
@@ -191,7 +190,6 @@
|
||||
latInput.value = parseFloat(place.lat).toFixed(4);
|
||||
lonInput.value = parseFloat(place.lon).toFixed(4);
|
||||
tzInput.value = '';
|
||||
tzHint.textContent = '';
|
||||
hideSuggestions();
|
||||
schedulePreview();
|
||||
}
|
||||
@@ -209,7 +207,6 @@
|
||||
latInput.value = pos.coords.latitude.toFixed(4);
|
||||
lonInput.value = pos.coords.longitude.toFixed(4);
|
||||
tzInput.value = '';
|
||||
tzHint.textContent = '';
|
||||
fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latInput.value}&lon=${lonInput.value}`, {
|
||||
headers: { 'User-Agent': USER_AGENT },
|
||||
})
|
||||
@@ -267,7 +264,6 @@
|
||||
_lastChartData = data;
|
||||
if (!tzInput.value && data.timezone) {
|
||||
tzInput.value = data.timezone;
|
||||
tzHint.textContent = 'Auto-detected from coordinates.';
|
||||
}
|
||||
setStatus('');
|
||||
confirmBtn.disabled = false;
|
||||
|
||||
@@ -71,8 +71,8 @@
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_tz">Timezone</label>
|
||||
<input id="id_nf_tz" name="tz" type="text"
|
||||
placeholder="auto-detected from location">
|
||||
<small id="id_nf_tz_hint"></small>
|
||||
placeholder="auto-detected from coordinates"
|
||||
readonly tabindex="-1">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -122,7 +122,6 @@
|
||||
const latInput = document.getElementById('id_nf_lat');
|
||||
const lonInput = document.getElementById('id_nf_lon');
|
||||
const tzInput = document.getElementById('id_nf_tz');
|
||||
const tzHint = document.getElementById('id_nf_tz_hint');
|
||||
const suggestions = document.getElementById('id_nf_suggestions');
|
||||
|
||||
const PREVIEW_URL = overlay.dataset.previewUrl;
|
||||
@@ -166,7 +165,7 @@
|
||||
if (data.place) placeInput.value = data.place;
|
||||
if (data.lat) latInput.value = data.lat;
|
||||
if (data.lon) lonInput.value = data.lon;
|
||||
if (data.tz) { tzInput.value = data.tz; tzHint.textContent = 'Auto-detected from coordinates.'; }
|
||||
if (data.tz) { tzInput.value = data.tz; }
|
||||
}
|
||||
|
||||
// ── Open / Close ──────────────────────────────────────────────────────────
|
||||
@@ -251,7 +250,6 @@
|
||||
latInput.value = parseFloat(place.lat).toFixed(4);
|
||||
lonInput.value = parseFloat(place.lon).toFixed(4);
|
||||
tzInput.value = '';
|
||||
tzHint.textContent = '';
|
||||
hideSuggestions();
|
||||
_saveForm();
|
||||
schedulePreview();
|
||||
@@ -270,7 +268,6 @@
|
||||
latInput.value = pos.coords.latitude.toFixed(4);
|
||||
lonInput.value = pos.coords.longitude.toFixed(4);
|
||||
tzInput.value = '';
|
||||
tzHint.textContent = '';
|
||||
fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${latInput.value}&lon=${lonInput.value}`, {
|
||||
headers: { 'User-Agent': USER_AGENT },
|
||||
})
|
||||
@@ -335,7 +332,6 @@
|
||||
// Back-fill timezone field from proxy response (first render)
|
||||
if (!tzInput.value && data.timezone) {
|
||||
tzInput.value = data.timezone;
|
||||
tzHint.textContent = 'Auto-detected from coordinates.';
|
||||
}
|
||||
|
||||
setStatus('');
|
||||
@@ -429,7 +425,6 @@
|
||||
latInput.value = '';
|
||||
lonInput.value = '';
|
||||
tzInput.value = '';
|
||||
tzHint.textContent = '';
|
||||
_lastChartData = null;
|
||||
confirmBtn.disabled = true;
|
||||
setStatus('');
|
||||
|
||||
Reference in New Issue
Block a user