From ba5f6556c05384b7423e2f0c3862278ffd7acf5b Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Fri, 8 May 2026 19:14:50 -0400 Subject: [PATCH] =?UTF-8?q?buddy=20btn=20sprint:=20banner-anchor=20+=20win?= =?UTF-8?q?dow.Brief=20fix=20lands=20the=20last=20red=20FT=20=E2=80=94=201?= =?UTF-8?q?6/16=20buddy=20+=2012=20share/jasmine/my=5Fnotes=20+=20818=20IT?= =?UTF-8?q?=20regression=20=E2=80=94=20TDD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two small fixes close out the OK→banner gap: 1. Anchor over h2: base.html drops
right before {% block content %} (after the messages block). note.js's showBanner now prefers the explicit anchor over the first

— keeps the banner in the visible content flow on pages where the first h2 is position:absolute (post.html's rotated navbar header was the immediate motivator; sky.html's rotated h2 is the same shape, so this catches that pre-emptively too). 2. window.Brief explicit assignment: const Brief = (...) at script-tag scope is reachable as a bare name but does NOT auto-attach to window. The buddy panel's OK handler gates banner reveal on `if (window.Brief && data.brief)` — that gate was always false, so Brief.showBanner never fired on share-OK even though the chip + Line append in DOM proved the fetch.then() was running. Explicit window.Brief = Brief; window.Note = Note; in note.js (post-IIFE) closes the gap. Also picks up the deferred page-object update — functional_tests.post_page.PostPage.share_post_with() now drives the buddy-btn flow (click #id_buddy_btn → type → click #id_buddy_panel .btn.btn-confirm → wait for recipient chip), so legacy SharingTest exercises the new pipeline end-to-end. NoteSpec.js T10 split into T10a/T10b: a covers the anchor-preferred path, b covers the

fallback. 16/16 buddy FTs green (previously 15/16). 12/12 sharing + Jasmine + my_notes FTs green. 818-test IT sweep green. Code architected by Disco DeDisco Git commit message Co-Authored-By: Claude Sonnet 4.6 --- .../dashboard/static/apps/dashboard/note.js | 18 +++++++++++++++--- src/functional_tests/post_page.py | 15 +++++++++++++-- src/static/tests/NoteSpec.js | 12 ++++++++++-- src/static_src/tests/NoteSpec.js | 12 ++++++++++-- src/templates/core/base.html | 5 +++++ 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/apps/dashboard/static/apps/dashboard/note.js b/src/apps/dashboard/static/apps/dashboard/note.js index efe838b..1b5fc27 100644 --- a/src/apps/dashboard/static/apps/dashboard/note.js +++ b/src/apps/dashboard/static/apps/dashboard/note.js @@ -44,9 +44,15 @@ const Brief = (() => { banner.remove(); }); - var h2 = document.querySelector('h2'); - if (h2 && h2.parentNode) { - h2.parentNode.insertBefore(banner, h2.nextSibling); + // Prefer the explicit anchor (set in base.html under the messages + // block, before {% block content %}) — keeps the banner in the + // visible content flow on pages where the first

is + // position:absolute (e.g. post.html's rotated navbar header). + // Falls back to

for pages that pre-date the anchor. + var anchor = document.getElementById('id_brief_banner_anchor') + || document.querySelector('h2'); + if (anchor && anchor.parentNode) { + anchor.parentNode.insertBefore(banner, anchor.nextSibling); } else { document.body.insertBefore(banner, document.body.firstChild); } @@ -67,3 +73,9 @@ const Brief = (() => { // Backwards-compat shim — to be removed once the codebase uniformly uses Brief. const Note = Brief; + +// `const Brief = (...)` at script-tag scope is reachable as a bare name but +// is NOT auto-attached to window — explicit assignment so callers that gate +// on `if (window.Brief)` (e.g. _buddy_panel.html's OK handler) succeed. +window.Brief = Brief; +window.Note = Note; diff --git a/src/functional_tests/post_page.py b/src/functional_tests/post_page.py index 846a27b..ab419ec 100644 --- a/src/functional_tests/post_page.py +++ b/src/functional_tests/post_page.py @@ -40,8 +40,19 @@ class PostPage: ) def share_post_with(self, email): - self.get_share_box().send_keys(email) - self.get_share_box().send_keys(Keys.ENTER) + # Buddy-btn flow (post-Brief sprint): click bottom-left handshake, + # type the email in the slide-out, click the .btn-confirm OK, wait + # for the recipient chip. + buddy_btn = self.test.browser.find_element(By.ID, "id_buddy_btn") + buddy_btn.click() + recipient = self.test.wait_for( + lambda: self.test.browser.find_element(By.ID, "id_recipient") + ) + recipient.send_keys(email) + ok = self.test.browser.find_element( + By.CSS_SELECTOR, "#id_buddy_panel .btn.btn-confirm" + ) + ok.click() self.test.wait_for( lambda: self.test.assertIn( email, [item.text for item in self.get_shared_with_list()] diff --git a/src/static/tests/NoteSpec.js b/src/static/tests/NoteSpec.js index d409fb6..97186e8 100644 --- a/src/static/tests/NoteSpec.js +++ b/src/static/tests/NoteSpec.js @@ -118,9 +118,17 @@ describe('Brief.showBanner', () => { expect(document.querySelector('.note-banner')).toBeNull(); }); - // ── T10 ── placement after h2 ───────────────────────────────────────────── + // ── T10 ── placement: anchor preferred over h2 ─────────────────────────── - it('T10: banner is inserted immediately after the first h2 in the document', () => { + it('T10a: banner is inserted as nextSibling of #id_brief_banner_anchor when present', () => { + const anchor = document.createElement('div'); + anchor.id = 'id_brief_banner_anchor'; + fixture.appendChild(anchor); + Brief.showBanner(SAMPLE_BRIEF); + expect(anchor.nextElementSibling.classList.contains('note-banner')).toBeTrue(); + }); + + it('T10b: falls back to inserting after the first h2 when anchor is absent', () => { Brief.showBanner(SAMPLE_BRIEF); const h2 = fixture.querySelector('h2'); expect(h2.nextElementSibling.classList.contains('note-banner')).toBeTrue(); diff --git a/src/static_src/tests/NoteSpec.js b/src/static_src/tests/NoteSpec.js index d409fb6..97186e8 100644 --- a/src/static_src/tests/NoteSpec.js +++ b/src/static_src/tests/NoteSpec.js @@ -118,9 +118,17 @@ describe('Brief.showBanner', () => { expect(document.querySelector('.note-banner')).toBeNull(); }); - // ── T10 ── placement after h2 ───────────────────────────────────────────── + // ── T10 ── placement: anchor preferred over h2 ─────────────────────────── - it('T10: banner is inserted immediately after the first h2 in the document', () => { + it('T10a: banner is inserted as nextSibling of #id_brief_banner_anchor when present', () => { + const anchor = document.createElement('div'); + anchor.id = 'id_brief_banner_anchor'; + fixture.appendChild(anchor); + Brief.showBanner(SAMPLE_BRIEF); + expect(anchor.nextElementSibling.classList.contains('note-banner')).toBeTrue(); + }); + + it('T10b: falls back to inserting after the first h2 when anchor is absent', () => { Brief.showBanner(SAMPLE_BRIEF); const h2 = fixture.querySelector('h2'); expect(h2.nextElementSibling.classList.contains('note-banner')).toBeTrue(); diff --git a/src/templates/core/base.html b/src/templates/core/base.html index e829894..a0ffefb 100644 --- a/src/templates/core/base.html +++ b/src/templates/core/base.html @@ -44,6 +44,11 @@ {% endfor %} {% endif %} + {# Anchor for Brief.showBanner — banner inserts as nextSibling so #} + {# it lands at the top of page content on every base.html-extending #} + {# page, regardless of where (or whether)

is positioned. #} +
+ {% block content %} {% endblock content %}