Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ written while it was being built. See [RELEASING.md](RELEASING.md).

### Changed

- For organizers, the "Participant View" section of a hackathon's sidebar is now
folded every time you open the hackathon. Open it with its arrow when you need
it.
- Manage Teams no longer has a "Suggest teams" button. Build teams by dragging
people onto them, or by downloading the spreadsheet, filling in the team
column and uploading it again.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@
// the default and start being what they are — the other side of the
// hackathon, one click away.
//
// Participant View is folded on every arrival, wherever that is: it neither
// opens for the route nor remembers being opened. Every link into a
// hackathon lands on Overview, a participant page, so opening the section
// around the lit row opened it for an owner on every visit. The chevron
// still opens it, and it stays open while this layout is mounted.
//
// Both are ignored for a participant: with no Manage section there is no
// heading and no chevron on the entries above it, so they are simply always
// out, exactly as before.
Expand Down Expand Up @@ -160,10 +166,6 @@
// one of the pages, not the way in. The way in is the heading, which is drawn
// whatever the fold state.
const insideManage = $derived(activeId?.startsWith('manage:') ?? false);
// Its counterpart, and deliberately not `!insideManage`: with no entry lit at
// all — a route neither section owns — neither section is forced open, and
// whatever the reader last chose stands.
const insideMember = $derived(activeId?.startsWith('member:') ?? false);

// `membership.role` is sourced from casbin. It is absent for a global admin who
// never joined, hence the second argument.
Expand All @@ -182,22 +184,17 @@
$effect(() => {
collapsed = stored('sidebar-collapsed') ?? collapsed;
manageOpen = stored('sidebar-manage-open') ?? manageOpen;
memberOpen = stored('sidebar-member-open') ?? memberOpen;
});

// Entering a section opens it, rather than pinning it open while you are in
// Entering Manage opens it, rather than pinning it open while you are in
// there: deriving the fold state from the route instead made the chevron a
// no-op on every Manage page, which reads as a broken control. Declared after
// the restore above so the route wins over the saved preference on arrival —
// the section holding the lit row is never the folded one.
// the restore above so the route wins over the saved preference on arrival.
// Participant View has no counterpart — see `memberOpen`.
$effect(() => {
if (insideManage) manageOpen = true;
});

$effect(() => {
if (insideMember) memberOpen = true;
});

$effect(() => {
if (typeof window === 'undefined') return;
const mq = window.matchMedia('(min-width: 768px)');
Expand Down Expand Up @@ -243,7 +240,6 @@

function toggleMember() {
memberOpen = !memberOpen;
remember('sidebar-member-open', memberOpen);
}
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ const OWNER = 1
* starts open — Settings included, since it is one of the screens in the
* section and not the way in — and Participant View starts folded, which is the
* whole point of labelling it: an owner arriving gets their own section, not a
* participant's. Arriving anywhere inside either section brings that one out, so
* the lit row is never hidden, and the chevron works wherever you are — an
* earlier version derived the state from the route and so dead-ended on every
* Manage page.
* participant's. Arriving inside Manage brings it out; Participant View stays
* folded wherever an owner arrives, Overview included, and does not remember
* being opened. The chevron works wherever you are — an earlier version derived
* the state from the route and so dead-ended on every Manage page.
*
* A participant has no Manage section, and so no heading and no chevron over
* their entries either: for them this is one plain list, exactly as before.
Expand Down Expand Up @@ -204,17 +204,29 @@ describe("HackathonSidebar's Participant View section", () => {
expect(memberEntry()).toBeInTheDocument()
})

// Never fold away the lit row: an owner who navigates to a participant page
// finds the section around it already open, whatever it was before.
it("opens itself when an owner is on a participant page", async () => {
// Every link into a hackathon lands on Overview, a participant page. Opening
// the section around the lit row there opened it on every visit.
it("stays folded for an owner arriving on a participant page", async () => {
await renderAt("/my/hackathon/h1/overview")

expect(memberEntry()).toBeInTheDocument()
expect(memberHeading()).toBeInTheDocument()
expect(memberEntry()).toBeNull()
})

it("closes again from the chevron while inside it", async () => {
// Earlier versions saved the choice; a browser still holding it is ignored.
it("ignores a saved preference to be open", async () => {
window.localStorage.setItem("sidebar-member-open", "true")
await renderAt("/my/hackathon/h1/overview")

expect(memberEntry()).toBeNull()
})

it("closes again from the chevron", async () => {
await renderAt("/my/hackathon/h1/overview")

await fireEvent.click(
screen.getByRole("button", { name: /Show Participant View/ }),
)
await fireEvent.click(
screen.getByRole("button", { name: /Hide Participant View/ }),
)
Expand Down
Loading