PICK SKY DEL btn: JS-inject after wheel paints so a blank modal carries no DEL action — TDD
Previously the DEL btn was always template-rendered inside .sky-wheel-col, which on a fresh PICK SKY modal (form pristine, schedulePreview not yet fired) put a red DEL btn floating in the empty wheel area suggesting there's something to delete when the user hasn't even seen a wheel yet. Refactored: drop the <button id="id_sky_delete_btn"> from _sky_overlay.html, lazily create it in JS via _ensureDelBtn() called from the schedulePreview success handler (right after SkyWheel.draw/redraw); the existing DEL click handler now also removes the btn from the DOM after clearing the SVG, so the next preview re-injects it. PickSkyRenderingTest.test_no_sky_delete_btn_in_blank_sky_select_modal IT asserts `id="id_sky_delete_btn"` doesn't appear in the rendered HTML for a SKY_SELECT room (the literal identifier still lives inside the inline <script> that does the injection — assertion targets the HTML-attribute-syntax form so the JS reference doesn't trip it). Existing PickSkyDelTest FT still green: it fires preview before clicking DEL, so the btn is present at click time. 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:
@@ -1792,6 +1792,19 @@ class PickSkyRenderingTest(TestCase):
|
|||||||
self.assertContains(response, 'id="id_pick_sky_btn"')
|
self.assertContains(response, 'id="id_pick_sky_btn"')
|
||||||
self.assertContains(response, 'style="display:none"')
|
self.assertContains(response, 'style="display:none"')
|
||||||
|
|
||||||
|
def test_no_sky_delete_btn_in_blank_sky_select_modal(self):
|
||||||
|
"""A fresh PICK SKY modal (no preview wheel rendered yet) must not
|
||||||
|
carry the DEL btn — it would otherwise float in the empty wheel area
|
||||||
|
suggesting there's something to delete when the user has only seen
|
||||||
|
the form. The JS schedulePreview success handler is the contract that
|
||||||
|
injects the btn after the wheel paints — so the rendered HTML should
|
||||||
|
carry no <button id="id_sky_delete_btn"> markup. (The literal string
|
||||||
|
does still appear inside the inline <script> that does the injection,
|
||||||
|
so the assertion targets the rendered attribute syntax, not the bare
|
||||||
|
identifier.)"""
|
||||||
|
response = self.client.get(self.url)
|
||||||
|
self.assertNotContains(response, 'id="id_sky_delete_btn"')
|
||||||
|
|
||||||
|
|
||||||
# ── SEA_SELECT rendering ──────────────────────────────────────────────────────
|
# ── SEA_SELECT rendering ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -87,10 +87,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# ── Wheel column ─────────────────────────────────────── #}
|
{# ── Wheel column ─────────────────────────────────────── #}
|
||||||
|
{# DEL btn is JS-injected after the wheel paints (see schedule #}
|
||||||
|
{# Preview success handler) — keeping it out of the template #}
|
||||||
|
{# means a blank PICK SKY modal can never show a DEL action #}
|
||||||
|
{# against a non-existent wheel. #}
|
||||||
<div class="sky-wheel-col">
|
<div class="sky-wheel-col">
|
||||||
<svg id="id_sky_svg" class="sky-svg"></svg>
|
<svg id="id_sky_svg" class="sky-svg"></svg>
|
||||||
<button type="button" id="id_sky_delete_btn"
|
|
||||||
class="btn btn-danger">DEL</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>{# /.sky-modal-body #}
|
</div>{# /.sky-modal-body #}
|
||||||
@@ -343,6 +345,7 @@
|
|||||||
} else {
|
} else {
|
||||||
SkyWheel.draw(svgEl, data);
|
SkyWheel.draw(svgEl, data);
|
||||||
}
|
}
|
||||||
|
_ensureDelBtn();
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
setStatus(`Could not fetch chart: ${err.message}`, 'error');
|
setStatus(`Could not fetch chart: ${err.message}`, 'error');
|
||||||
@@ -401,20 +404,26 @@
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── DEL btn — clears wheel + form + localStorage (no server hit) ────────
|
// ── DEL btn — JS-injected after the wheel paints; absent on a blank modal
|
||||||
// PICK SKY's wheel is a live preview; un-saved data lives only in LS_KEY.
|
// PICK SKY's wheel is a live preview; un-saved data lives only in LS_KEY.
|
||||||
// Match the My Sky applet / Dashsky pattern but skip the server delete —
|
// The btn is created lazily after the first SkyWheel.draw so a blank modal
|
||||||
// there's no Character draft created during preview (sky_save fires only
|
// can never offer a DEL action against a non-existent wheel; clearing the
|
||||||
// on SAVE SKY click w. action='confirm').
|
// SVG removes the btn from the DOM entirely (re-injected on next preview).
|
||||||
|
|
||||||
// window.showGuard is assigned in a base.html script that loads BELOW the
|
let _delBtn = null;
|
||||||
// content block — defer the readiness check to click-time so the listener
|
function _ensureDelBtn() {
|
||||||
// bind happens regardless of inline-script execution order.
|
if (_delBtn) return;
|
||||||
const delBtn = document.getElementById('id_sky_delete_btn');
|
const wheelCol = document.querySelector('.sky-wheel-col');
|
||||||
if (delBtn) {
|
if (!wheelCol) return;
|
||||||
delBtn.addEventListener('click', () => {
|
_delBtn = document.createElement('button');
|
||||||
|
_delBtn.type = 'button';
|
||||||
|
_delBtn.id = 'id_sky_delete_btn';
|
||||||
|
_delBtn.className = 'btn btn-danger';
|
||||||
|
_delBtn.textContent = 'DEL';
|
||||||
|
wheelCol.appendChild(_delBtn);
|
||||||
|
_delBtn.addEventListener('click', () => {
|
||||||
if (!window.showGuard) return;
|
if (!window.showGuard) return;
|
||||||
window.showGuard(delBtn, 'Forget sky?', () => {
|
window.showGuard(_delBtn, 'Forget sky?', () => {
|
||||||
while (svgEl.firstChild) svgEl.removeChild(svgEl.firstChild);
|
while (svgEl.firstChild) svgEl.removeChild(svgEl.firstChild);
|
||||||
form.reset();
|
form.reset();
|
||||||
latInput.value = '';
|
latInput.value = '';
|
||||||
@@ -425,6 +434,10 @@
|
|||||||
confirmBtn.disabled = true;
|
confirmBtn.disabled = true;
|
||||||
setStatus('');
|
setStatus('');
|
||||||
try { localStorage.removeItem(LS_KEY); } catch (_) {}
|
try { localStorage.removeItem(LS_KEY); } catch (_) {}
|
||||||
|
if (_delBtn) {
|
||||||
|
_delBtn.remove();
|
||||||
|
_delBtn = null;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user