Fix trapped scrollbars app-wide, keep the week calendar as its own scroll
Several overflow-x-auto wrappers (table cards, a text preview) got an accidental *vertical* scrollbar too -- per the CSS overflow spec, setting only overflow-x to a non-visible value forces the other axis to compute as auto if left unset, so any of these taller than the viewport were trapped scrolling independently of the page (Safari showed it plainly; other browsers hid it more subtly). Fixed everywhere with overflow-y-visible, except the week calendar. The week grid genuinely needs to stay its own bounded, contained scroll: it always spans the full 24h day (never clipped, by design), so folding it into the page's own scroll would mean scrolling past a screenful of empty early hours most weeks. Instead it's capped to 70vh with a real overflow-y-auto, and a small script scrolls it to just before the week's first event on load (events.services.calendar.week_grid now reports first_event_hour) -- no more landing on an empty view by default. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECGMEwrc2k4D8VQuwjstj9
This commit is contained in:
@@ -29,6 +29,6 @@
|
||||
<iframe class="h-[560px] w-full rounded-lg border border-line bg-white" src="{{ render_url }}" loading="lazy" title="{{ preview.label }}"></iframe>
|
||||
</div>
|
||||
{% if show_text_toggle %}
|
||||
<pre class="hidden overflow-x-auto p-4.5 font-mono text-sm whitespace-pre-wrap text-ink" data-view-panel="text">{{ preview.text }}</pre>
|
||||
<pre class="hidden overflow-x-auto overflow-y-visible p-4.5 font-mono text-sm whitespace-pre-wrap text-ink" data-view-panel="text">{{ preview.text }}</pre>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto overflow-y-visible">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
{% for object in object_list %}
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
{% block panel %}
|
||||
{% if view_mode == "list" %}
|
||||
<div class="card overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto overflow-y-visible">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -158,7 +158,17 @@
|
||||
<div class="border-b border-line px-4 py-2.5 font-display text-sm font-extrabold tracking-[.08em] text-ink uppercase">
|
||||
{% blocktrans with start=calendar.week_start|date:"j M" end=calendar.week_end|date:"j M Y" %}{{ start }} – {{ end }}{% endblocktrans %}
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
{% comment %}
|
||||
Deliberately its own bounded, contained scroll (max-h + overflow-y-auto)
|
||||
rather than riding the page's own scroll like everywhere else in this app
|
||||
(management/base.html's own comment) -- the grid always spans the full
|
||||
24h day (never clipped, see DEFAULT_DAY_START_HOUR/END_HOUR's own
|
||||
comment), so inlining all of it into the page would mean scrolling past
|
||||
a screenful of empty small-hours before reaching any events most weeks.
|
||||
extra_body's own script scrolls this container to just before the
|
||||
week's first event on load, for the same reason.
|
||||
{% endcomment %}
|
||||
<div id="cal-week-scroll" class="max-h-[70vh] overflow-x-auto overflow-y-auto" data-first-event-hour="{{ calendar.first_event_hour|default_if_none:"" }}" data-day-start-hour="{{ calendar.day_start_hour }}" data-day-end-hour="{{ calendar.day_end_hour }}">
|
||||
{% with hours_span=calendar.hours|length|add:"-1" %}
|
||||
<div class="cal-week-inner">
|
||||
<div class="cal-week-header">
|
||||
@@ -277,3 +287,25 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock panel %}
|
||||
|
||||
{% block extra_body %}
|
||||
{% if calendar_range == "week" %}
|
||||
<script>
|
||||
(() => {
|
||||
// Scrolls #cal-week-scroll to just before the week's first event on
|
||||
// load -- the grid always spans the full 24h day (see week_grid's own
|
||||
// comment), so without this most weeks would open on an empty
|
||||
// small-hours view. No-op when the week has no events at all
|
||||
// (data-first-event-hour is blank then -- nothing to reveal either way).
|
||||
const container = document.getElementById("cal-week-scroll");
|
||||
const body = container?.querySelector(".cal-week-body");
|
||||
if (!container || !body || container.dataset.firstEventHour === "") return;
|
||||
|
||||
const dayStart = Number(container.dataset.dayStartHour);
|
||||
const dayEnd = Number(container.dataset.dayEndHour);
|
||||
const targetHour = Math.max(Number(container.dataset.firstEventHour) - 1, dayStart);
|
||||
container.scrollTop = ((targetHour - dayStart) / (dayEnd - dayStart)) * body.offsetHeight;
|
||||
})();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock extra_body %}
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-base">{% lucide "calendar" size=18 %} {% trans "Occurrences" %}</h2>
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto overflow-y-visible">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
{% block panel %}
|
||||
<div class="card overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto overflow-y-visible">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto overflow-y-visible">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
{% block panel %}
|
||||
<div class="card overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto overflow-y-visible">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<h2 class="card-title text-base">{% lucide "plus" size=18 %} {% blocktrans count counter=plan.to_create|length %}{{ counter }} game to create{% plural %}{{ counter }} games to create{% endblocktrans %}</h2>
|
||||
|
||||
{% if plan.to_create %}
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto overflow-y-visible">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -69,7 +69,7 @@
|
||||
<h2 class="card-title text-base">{% lucide "refresh-cw" size=18 %} {% blocktrans count counter=plan.to_update|length %}{{ counter }} game to update{% plural %}{{ counter }} games to update{% endblocktrans %}</h2>
|
||||
|
||||
{% if plan.to_update %}
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto overflow-y-visible">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -5434,6 +5434,24 @@ class EventManagementTests(ManagementTestBase):
|
||||
|
||||
self.assertContains(response, 'aria-label="Part of a series"', count=1)
|
||||
|
||||
def test_the_week_calendar_carries_the_first_event_hour_for_the_initial_scroll(self):
|
||||
local_today = timezone.localdate()
|
||||
start = timezone.make_aware(datetime.datetime.combine(local_today, datetime.time(18, 0)))
|
||||
event = Event.objects.create(club=self.club, title="Evening practice", start=start)
|
||||
event.teams.add(self.own_team)
|
||||
self.client.force_login(self.own_team_coach)
|
||||
|
||||
response = self.club_get("event_list") # default view=calendar, range=week
|
||||
|
||||
self.assertContains(response, 'data-first-event-hour="18"')
|
||||
|
||||
def test_the_week_calendar_first_event_hour_is_blank_with_no_events(self):
|
||||
self.client.force_login(self.own_team_coach)
|
||||
|
||||
response = self.club_get("event_list", params={"date": "2030-01-07"}) # a Monday with nothing scheduled
|
||||
|
||||
self.assertContains(response, 'data-first-event-hour=""')
|
||||
|
||||
def test_the_month_calendar_marks_a_series_occurrence_with_the_repeat_icon(self):
|
||||
series = EventSeries.objects.create(club=self.club, title="Weekly Training", kind=Event.EventKind.TRAINING, dtstart=timezone.now(), rrule="FREQ=WEEKLY;COUNT=1")
|
||||
occurrence = Event.objects.create(club=self.club, title="Weekly Training", start=timezone.now(), series=series)
|
||||
|
||||
Reference in New Issue
Block a user