Skip to content

Commit 48cd9ce

Browse files
committed
fix(programa): debug-friendly filter button binding
Switch from event delegation to a direct click handler bound to the button via getElementById. Adds try/catch + console.log statements so we can see in the browser console whether the script is running, the button is being found, and clicks are reaching the handler. The button now has id='favorites-filter-btn' (was data-filter-favorites). The script tries to bind the handler: - immediately (in case the DOM is ready) - on DOMContentLoaded (in case the script runs before DOM) - on astro:page-load (in case the page is a view transition) - guarded with dataset.bound so it only runs once If the filter still doesn't work, the browser console will show which of these steps is failing.
1 parent df69d2d commit 48cd9ce

1 file changed

Lines changed: 71 additions & 62 deletions

File tree

src/components/ProgramaPage.astro

Lines changed: 71 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ const schedule = await getSchedule()
5151
<div class="max-w-3xl mx-auto mb-6 flex justify-end">
5252
<button
5353
type="button"
54+
id="favorites-filter-btn"
5455
class="favorites-filter-btn inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 text-sm text-pycon-gray-25 hover:border-pycon-orange/50 hover:text-pycon-orange transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-pycon-yellow aria-pressed:bg-pycon-orange aria-pressed:text-pycon-black aria-pressed:font-semibold aria-pressed:border-pycon-orange"
5556
aria-pressed="false"
5657
aria-label={t.list.favoritesOnly}
57-
data-filter-favorites
5858
data-filter-on-prefix={t.list.filterOnPrefix}
5959
data-filter-on-singular={t.list.filterOnSingular}
6060
data-filter-on-plural={t.list.filterOnPlural}
@@ -151,6 +151,10 @@ const schedule = await getSchedule()
151151
<script is:inline src="https://pretalx.com/pycones-2026/widgets/schedule.js"></script>
152152

153153
<script is:inline>
154+
try {
155+
console.log('[programa] script starting')
156+
} catch (e) {}
157+
154158
;(function () {
155159
// === View toggle (list <-> grid) ===
156160
;(function () {
@@ -180,10 +184,6 @@ const schedule = await getSchedule()
180184

181185
try {
182186
const saved = localStorage.getItem(STORAGE_KEY)
183-
// Only restore the saved view if it matches the HTML default. Legacy
184-
// values (e.g. 'list' from when list was the default) are ignored so
185-
// the page always opens with the current default and there's no jump
186-
// from a stale saved value.
187187
const defaultChecked = document.querySelector('input[name="schedule-view"]:checked')
188188
const defaultValue = defaultChecked ? defaultChecked.value : null
189189
if ((saved === 'list' || saved === 'grid') && saved === defaultValue) {
@@ -192,11 +192,7 @@ const schedule = await getSchedule()
192192
} catch (e) {}
193193
})()
194194

195-
// === Favorites sync (shared with the Pretalx widget) ===
196-
// The widget persists favourites under `pycones-2026_favs` as a JSON
197-
// array of session codes. We read/write the same key so toggling in
198-
// the list is reflected in the widget (and vice-versa, next time the
199-
// widget re-reads on mount).
195+
// === Favorites sync + filter (shared with the Pretalx widget) ===
200196
;(function () {
201197
const FAV_KEY = 'pycones-2026_favs'
202198

@@ -247,59 +243,70 @@ const schedule = await getSchedule()
247243
empty.hidden = !(filterActive && count === 0)
248244
}
249245

250-
// Click handler: handle both the per-session favourite button and
251-
// the "only favourites" filter button. Event delegation on document
252-
// so we don't depend on either button being in the DOM at script
253-
// init time.
254-
document.addEventListener('click', (e) => {
255-
const target = e.target.closest && e.target.closest('button')
256-
if (!target) return
257-
258-
// Per-session favourite toggle
259-
const favBtn = target.closest('.favorite-btn')
260-
if (favBtn) {
261-
e.preventDefault()
262-
const code = favBtn.getAttribute('data-session-code')
263-
if (!code) return
264-
const favs = readFavs()
265-
const idx = favs.indexOf(code)
266-
let added
267-
if (idx >= 0) {
268-
favs.splice(idx, 1)
269-
added = false
270-
} else {
271-
favs.push(code)
272-
added = true
273-
}
274-
writeFavs(favs)
275-
syncFavorites()
276-
announce(added ? favBtn.dataset.addedMessage : favBtn.dataset.removedMessage)
246+
// Click handler for the filter button. Bound directly to the button
247+
// (not event delegation) so it works regardless of any capture-phase
248+
// listeners the widget or other code may have installed.
249+
function handleFilterClick(e) {
250+
if (e) e.preventDefault()
251+
const btn = document.getElementById('favorites-filter-btn')
252+
if (!btn) {
253+
try {
254+
console.warn('[programa] filter button not in DOM')
255+
} catch (e) {}
277256
return
278257
}
258+
const wasPressed = btn.getAttribute('aria-pressed') === 'true'
259+
const nowPressed = !wasPressed
260+
btn.setAttribute('aria-pressed', nowPressed ? 'true' : 'false')
261+
const listView = document.getElementById('schedule-list-view')
262+
if (listView) listView.classList.toggle('filter-favorites-only', nowPressed)
263+
updateEmptyState()
264+
const count = readFavs().length
265+
const word = count === 1 ? btn.dataset.filterOnSingular : btn.dataset.filterOnPlural
266+
const message = nowPressed
267+
? count > 0
268+
? `${btn.dataset.filterOnPrefix}. Mostrando ${count} ${word}.`
269+
: `${btn.dataset.filterOnPrefix}.`
270+
: btn.dataset.filterOffMessage
271+
announce(message)
272+
try {
273+
console.log('[programa] filter toggled, nowPressed=', nowPressed)
274+
} catch (e) {}
275+
}
279276

280-
// "Only favourites" filter toggle
281-
const filterBtn = target.closest('[data-filter-favorites]')
282-
if (filterBtn) {
283-
e.preventDefault()
284-
const wasPressed = filterBtn.getAttribute('aria-pressed') === 'true'
285-
const nowPressed = !wasPressed
286-
filterBtn.setAttribute('aria-pressed', nowPressed ? 'true' : 'false')
287-
const listView = document.getElementById('schedule-list-view')
288-
if (listView) listView.classList.toggle('filter-favorites-only', nowPressed)
289-
updateEmptyState()
290-
const count = readFavs().length
291-
const word = count === 1 ? filterBtn.dataset.filterOnSingular : filterBtn.dataset.filterOnPlural
292-
const message = nowPressed
293-
? count > 0
294-
? `${filterBtn.dataset.filterOnPrefix}. Mostrando ${count} ${word}.`
295-
: `${filterBtn.dataset.filterOnPrefix}.`
296-
: filterBtn.dataset.filterOffMessage
297-
announce(message)
277+
function bindFilterButton() {
278+
const btn = document.getElementById('favorites-filter-btn')
279+
if (!btn) return
280+
if (btn.dataset.bound === '1') return
281+
btn.addEventListener('click', handleFilterClick)
282+
btn.dataset.bound = '1'
283+
try {
284+
console.log('[programa] filter button bound')
285+
} catch (e) {}
286+
}
287+
288+
// Per-session favourite click handler (event delegation)
289+
document.addEventListener('click', (e) => {
290+
const favBtn = e.target.closest && e.target.closest('.favorite-btn')
291+
if (!favBtn) return
292+
e.preventDefault()
293+
const code = favBtn.getAttribute('data-session-code')
294+
if (!code) return
295+
const favs = readFavs()
296+
const idx = favs.indexOf(code)
297+
let added
298+
if (idx >= 0) {
299+
favs.splice(idx, 1)
300+
added = false
301+
} else {
302+
favs.push(code)
303+
added = true
298304
}
305+
writeFavs(favs)
306+
syncFavorites()
307+
announce(added ? favBtn.dataset.addedMessage : favBtn.dataset.removedMessage)
299308
})
300309

301-
// Write a message to the live region. Clears + forces a reflow so
302-
// identical back-to-back messages still re-announce.
303310
function announce(message) {
304311
if (!message) return
305312
const status = document.getElementById('favorite-status')
@@ -309,16 +316,18 @@ const schedule = await getSchedule()
309316
status.textContent = message
310317
}
311318

312-
// Re-sync when the user switches to the list view (widget may have
313-
// mutated localStorage in the same session).
314319
document.addEventListener('pycones:view-changed', syncFavorites)
315-
316-
// Re-sync on cross-tab/window changes
317320
window.addEventListener('storage', (e) => {
318321
if (e.key === FAV_KEY) syncFavorites()
319322
})
323+
document.addEventListener('astro:page-load', bindFilterButton)
324+
325+
// Try to bind now, and also on DOM ready
326+
bindFilterButton()
327+
if (document.readyState === 'loading') {
328+
document.addEventListener('DOMContentLoaded', bindFilterButton)
329+
}
320330

321-
// Initial render
322331
syncFavorites()
323332
})()
324333

0 commit comments

Comments
 (0)