Move section collapse state to URL query params#1958
Conversation
Section expand/collapse state is now tracked in URL query params (collapsed/expanded) instead of persisting directly to the DB on every toggle. The DB-stored collapsed field on DashboardContainer becomes the default fallback for when someone opens a dashboard fresh (no URL state). - Chevron click updates URL state only (per-viewer, shareable) - "Collapse by Default" / "Expand by Default" menu action saves to DB - SectionHeader accepts separate collapsed/defaultCollapsed props and onToggle/onToggleDefaultCollapsed handlers - Add 7 unit tests for SectionHeader
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR Review
Otherwise the implementation is clean: URL state logic with |
E2E Test Results✅ All tests passed • 92 passed • 3 skipped • 962s
Tests ran across 4 shards in parallel. |
Knip - Unused Code Analysis⚪ 0 change in total issues (240 on main → 240 on PR)
What is this?Knip finds unused files, dependencies, and exports in your codebase. Run |
| @@ -1102,16 +1102,11 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) { | |||
| const [editedTile, setEditedTile] = useState<undefined | Tile>(); | |||
|
|
|||
| const onAddTile = (containerId?: string) => { | |||
There was a problem hiding this comment.
i think this whole hook should be moved below the isSectionCollapsed hook to avoid potential weird behavior due to hoisting
There was a problem hiding this comment.
Done — moved onAddTile below isSectionCollapsed and handleToggleSection so it can reference both without hoisting concerns. See c72626e.
| setUrlExpandedIds(prev => { | ||
| const next = [...(prev ?? [])]; | ||
| if (!next.includes(containerId)) next.push(containerId); | ||
| return next.length > 0 ? next : null; | ||
| }); | ||
| setUrlCollapsedIds(prev => { | ||
| const next = (prev ?? []).filter(id => id !== containerId); | ||
| return next.length > 0 ? next : null; | ||
| }); | ||
| } else { | ||
| // Collapsing: add to collapsed list, remove from expanded list | ||
| setUrlCollapsedIds(prev => { | ||
| const next = [...(prev ?? [])]; | ||
| if (!next.includes(containerId)) next.push(containerId); | ||
| return next.length > 0 ? next : null; | ||
| }); | ||
| setUrlExpandedIds(prev => { | ||
| const next = (prev ?? []).filter(id => id !== containerId); | ||
| return next.length > 0 ? next : null; | ||
| }); |
There was a problem hiding this comment.
These functions are similar enough they should be abstracted.
1 function that pushes a new container id to the existing array
1 function that filters out the old containerId
Additionally, it would be worth using produce via immer as we do elsewhere in the app
There was a problem hiding this comment.
Done — extracted addToUrlSet and removeFromUrlSet helpers using produce via immer. handleToggleSection now calls these instead of duplicating the add/filter logic, and uses isSectionCollapsed directly instead of re-implementing the lookup. See c72626e.
…with immer - Move onAddTile below isSectionCollapsed/handleToggleSection to avoid hoisting issues (knudtty review comment) - Extract addToUrlSet/removeFromUrlSet helpers using immer produce, eliminating duplicated add/filter logic in handleToggleSection - Use isSectionCollapsed directly instead of re-implementing the lookup
|
I find it a bit counterintuitive how different the 2 ways of adding a tile are (small icon to add to a panel, huge button to add a tile) but we can loop in design and address in a follow up! Nice work |
## Summary - Section expand/collapse is now tracked in URL query params (`collapsed`/`expanded`) instead of persisting to the DB on every chevron click - The DB-stored `collapsed` field on `DashboardContainer` becomes the default fallback — what viewers see when opening a dashboard fresh (no URL state) - Chevron click updates URL state only (per-viewer, shareable via link) - "Collapse by Default" / "Expand by Default" menu action in the section header saves to the DB (via `setDashboard`), setting the default for all viewers - `SectionHeader` now accepts separate `collapsed`/`defaultCollapsed` props and `onToggle`/`onToggleDefaultCollapsed` handlers - Adds 7 unit tests for `SectionHeader` Implements Drew's [review feedback on PR #1926](#1926 (comment)): > IMO, expanding/collapsing should not be persisted to the dashboard UNLESS this option is used. [...] I think it would be nice to persist normal expand collapse states in the URL, and then fallback to the default state (saved in the DB based on this option here) if there is no URL state. ## Demo  Shows: expanded sections → chevron click collapses first section (URL updates to `?collapsed=...`) → menu shows "Collapse by Default" (DB action, separate from view state) ## Test plan - [x] Open a dashboard with sections — collapse/expand via chevron click, verify URL updates (`?collapsed=...` / `?expanded=...`) without saving to DB - [x] Copy the URL with collapse state and open in a new tab — verify sections reflect the URL state - [x] Open the section menu and click "Collapse by Default" — verify this saves to DB (persists after page refresh without URL params) - [x] Verify "Expand by Default" / "Collapse by Default" label reflects the DB default, not current view state - [x] Run `yarn ci:unit --testPathPatterns='SectionHeader'` — all 7 tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary - Section expand/collapse is now tracked in URL query params (`collapsed`/`expanded`) instead of persisting to the DB on every chevron click - The DB-stored `collapsed` field on `DashboardContainer` becomes the default fallback — what viewers see when opening a dashboard fresh (no URL state) - Chevron click updates URL state only (per-viewer, shareable via link) - "Collapse by Default" / "Expand by Default" menu action in the section header saves to the DB (via `setDashboard`), setting the default for all viewers - `SectionHeader` now accepts separate `collapsed`/`defaultCollapsed` props and `onToggle`/`onToggleDefaultCollapsed` handlers - Adds 7 unit tests for `SectionHeader` Implements Drew's [review feedback on PR #1926](#1926 (comment)): > IMO, expanding/collapsing should not be persisted to the dashboard UNLESS this option is used. [...] I think it would be nice to persist normal expand collapse states in the URL, and then fallback to the default state (saved in the DB based on this option here) if there is no URL state. ## Demo  Shows: expanded sections → chevron click collapses first section (URL updates to `?collapsed=...`) → menu shows "Collapse by Default" (DB action, separate from view state) ## Test plan - [x] Open a dashboard with sections — collapse/expand via chevron click, verify URL updates (`?collapsed=...` / `?expanded=...`) without saving to DB - [x] Copy the URL with collapse state and open in a new tab — verify sections reflect the URL state - [x] Open the section menu and click "Collapse by Default" — verify this saves to DB (persists after page refresh without URL params) - [x] Verify "Expand by Default" / "Collapse by Default" label reflects the DB default, not current view state - [x] Run `yarn ci:unit --testPathPatterns='SectionHeader'` — all 7 tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: peter-leonov-ch <209667683+peter-leonov-ch@users.noreply.github.com>
Summary
collapsed/expanded) instead of persisting to the DB on every chevron clickcollapsedfield onDashboardContainerbecomes the default fallback — what viewers see when opening a dashboard fresh (no URL state)setDashboard), setting the default for all viewersSectionHeadernow accepts separatecollapsed/defaultCollapsedprops andonToggle/onToggleDefaultCollapsedhandlersSectionHeaderImplements Drew's review feedback on PR #1926:
Demo
Shows: expanded sections → chevron click collapses first section (URL updates to
?collapsed=...) → menu shows "Collapse by Default" (DB action, separate from view state)Test plan
?collapsed=.../?expanded=...) without saving to DByarn ci:unit --testPathPatterns='SectionHeader'— all 7 tests pass🤖 Generated with Claude Code