Skip to content

Add sidebar sorting and folder collapse controls - #198

Open
Norkep wants to merge 4 commits into
erictli:mainfrom
Norkep:agent/sidebar-sorting-collapse
Open

Add sidebar sorting and folder collapse controls#198
Norkep wants to merge 4 commits into
erictli:mainfrom
Norkep:agent/sidebar-sorting-collapse

Conversation

@Norkep

@Norkep Norkep commented Aug 3, 2026

Copy link
Copy Markdown

Summary

  • Add newest/oldest note sorting to the sidebar.
  • Apply the same stable ordering to flat and folder views.
  • Keep pinned notes ahead of unpinned notes.
  • Persist the selected note sort order in Scratch settings through .scratch/settings.json.
  • Add a collapsible Folders section with accessible labels and keyboard behavior.
  • Persist the Folders section's collapsed state locally on the current device through localStorage.
  • Keep sorting and collapsing view-only; no note or folder is moved on disk.

Verification

  • npm test -- --run: 4 files, 8 tests passed.
  • npm run build: passed.
  • cargo test --manifest-path src-tauri/Cargo.toml --quiet: 1 test passed.
  • cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets --all-features -- -D warnings: passed.
  • git diff --check origin/main...HEAD: passed.

Tests cover newest/oldest ordering, deterministic ties, identical ordering in flat and folder views, persisted folder-section collapse, accessible controls, and keyboard-navigation exclusion while the section is collapsed.

Scope

This is PR 1 and the base of the Scratch 1.0.1 backport stack. Editor preferences, standalone windows, workspaces, formatting menus, tables, and drag/drop remain outside this change.

No zero-bug guarantee is possible, but the branch is independent, buildable, and covered by focused regression tests.

Summary by CodeRabbit

  • New Features

    • Added newest-first and oldest-first sorting for sidebar notes.
    • Pinned notes remain prioritized, with sorting preferences saved between sessions.
    • Added a collapsible Folders section with persistent state and improved keyboard accessibility.
    • Folder visibility and navigation now reflect collapsed sections.
  • Bug Fixes

    • Added safe fallback and rollback behavior when sidebar preferences cannot be saved.
  • Tests

    • Added comprehensive coverage for sorting, persistence, folder behavior, accessibility, and keyboard navigation.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The sidebar now supports persisted newest-first or oldest-first note sorting and a persisted collapsible folders section. Vitest, happy-dom, and tests cover sorting, settings serialization, menu behavior, disclosure controls, and folder visibility.

Changes

Sidebar navigation behavior

Layer / File(s) Summary
Sorting contracts and tree behavior
src/types/note.ts, src-tauri/src/lib.rs, src/lib/folderTree.ts, src/lib/folderTree.test.ts, package.json, vitest.config.ts, src/test/setup.ts
Adds NoteSortOrder, persists sidebarSortOrder, applies deterministic pinned-note and modified-date sorting, and configures Vitest.
Sort menu and sidebar integration
src/components/layout/SidebarControls.tsx, src/components/layout/SidebarControls.test.tsx, src/components/layout/Sidebar.tsx, src/services/notes.ts, src/components/notes/NoteList.tsx
Adds the sort menu, restores and saves the selected order, rolls back failed saves, and passes the order to note rendering.
Persisted folder-section collapse
src/components/layout/SidebarFolderSection.tsx, src/components/layout/SidebarFolderSection.test.tsx, src/components/notes/FolderTreeView.tsx, src/components/notes/FolderTreeView.test.tsx
Adds persisted folder-section collapse state, accessible disclosure controls, visibility-aware navigation, expansion behavior, and related tests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant NoteSortMenu
  participant Sidebar
  participant updateSidebarSortOrder
  participant NoteList
  User->>NoteSortMenu: Select newest or oldest
  NoteSortMenu->>Sidebar: onChange(sortOrder)
  Sidebar->>updateSidebarSortOrder: Save sort order
  updateSidebarSortOrder-->>Sidebar: Save result
  Sidebar->>NoteList: Pass sortOrder
  NoteList-->>User: Render sorted notes
Loading

Possibly related PRs

Suggested reviewers: erictli

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the pull request's main changes: sidebar sorting and folder-collapse controls.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/layout/Sidebar.tsx (1)

172-209: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Avoid read-modify-write on the full Settings object.

updateSettings(newSettings) replaces the in-memory Settings state and writes that object to .scratch/settings.json. handleNoteSortOrderChange reads settings, then writes only sidebarSortOrder, so any overlapping frontend update that rewrites the same full object can drop the sidebar sort change; examples include pin/unpin settings updates and other getSettings()-then-updateSettings() paths. Add a Rust-side field patch command such as update_sidebar_sort_order, or serialize frontend settings writes so they cannot interleave.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/Sidebar.tsx` around lines 172 - 209, Update
handleNoteSortOrderChange and the settings update flow to avoid full Settings
read-modify-write for sidebarSortOrder. Prefer adding and invoking a Rust-side
field patch command such as update_sidebar_sort_order so concurrent settings
updates cannot overwrite this change; otherwise serialize all frontend
getSettings-then-updateSettings writes, including pin/unpin paths.
🧹 Nitpick comments (1)
src/components/notes/FolderTreeView.test.tsx (1)

1-67: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for section-reopening and keyboard-visibility behavior.

This file only tests that FolderItemComponent exposes no descendant-collapse action. It does not test FolderTreeView's expandFolder behavior, which resets foldersSectionCollapsed to false so that drag-and-drop, subfolder creation, and search navigation all reopen a collapsed Folders section. It also does not test that getVisibleItemsForFolderSection excludes folder items from keyboard navigation while the section is collapsed.

The PR objective calls out "focused tests for ... keyboard navigation" for this layer. Add a test that collapses the section, triggers an expand-folder event (or calls the exposed reopening path), and asserts the section becomes expanded and keyboard navigation reaches folder items again.

Do you want me to generate this test?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/notes/FolderTreeView.test.tsx` around lines 1 - 67, Extend
FolderTreeView tests beyond FolderItemComponent to cover section reopening:
collapse the Folders section, trigger the expandFolder/“expand-folder” path, and
assert foldersSectionCollapsed becomes false. Also verify
getVisibleItemsForFolderSection excludes folder items while collapsed and
includes them after reopening, confirming keyboard navigation can reach folders
again.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/layout/SidebarFolderSection.tsx`:
- Around line 4-35: The PR objective must accurately describe
foldersSectionCollapsed persistence: update it to say device/local storage
because loadFolderSectionCollapsed and saveFolderSectionCollapsed use
globalThis.localStorage, or alternatively add foldersSectionCollapsed to the
Settings model and route persistence through notesService.updateSettings if it
must be exported or synced with Scratch settings.

---

Outside diff comments:
In `@src/components/layout/Sidebar.tsx`:
- Around line 172-209: Update handleNoteSortOrderChange and the settings update
flow to avoid full Settings read-modify-write for sidebarSortOrder. Prefer
adding and invoking a Rust-side field patch command such as
update_sidebar_sort_order so concurrent settings updates cannot overwrite this
change; otherwise serialize all frontend getSettings-then-updateSettings writes,
including pin/unpin paths.

---

Nitpick comments:
In `@src/components/notes/FolderTreeView.test.tsx`:
- Around line 1-67: Extend FolderTreeView tests beyond FolderItemComponent to
cover section reopening: collapse the Folders section, trigger the
expandFolder/“expand-folder” path, and assert foldersSectionCollapsed becomes
false. Also verify getVisibleItemsForFolderSection excludes folder items while
collapsed and includes them after reopening, confirming keyboard navigation can
reach folders again.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 85e3075e-e578-487d-a12c-99e972348292

📥 Commits

Reviewing files that changed from the base of the PR and between 9126a5a and a617a4e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • package.json
  • src-tauri/src/lib.rs
  • src/components/layout/Sidebar.tsx
  • src/components/layout/SidebarControls.test.tsx
  • src/components/layout/SidebarControls.tsx
  • src/components/layout/SidebarFolderSection.test.tsx
  • src/components/layout/SidebarFolderSection.tsx
  • src/components/notes/FolderTreeView.test.tsx
  • src/components/notes/FolderTreeView.tsx
  • src/components/notes/NoteList.tsx
  • src/lib/folderTree.test.ts
  • src/lib/folderTree.ts
  • src/types/note.ts
  • vitest.config.ts

Comment thread src/components/layout/SidebarFolderSection.tsx

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/layout/SidebarControls.test.tsx`:
- Around line 63-80: Replace the innerHTML assertion in the “does not prevent
Radix focus restoration on the sort trigger” test with a behavioral
focus-restoration check: open and close the sort menu, then assert that focus
returns to the sort trigger. Use the existing NoteSortMenu interaction path and
preserve cleanup of the rendered root.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 672b603c-c20b-4e9f-aacc-532d9dcb49ed

📥 Commits

Reviewing files that changed from the base of the PR and between a617a4e and 4fbbf8b.

📒 Files selected for processing (14)
  • src-tauri/src/lib.rs
  • src/components/layout/Sidebar.tsx
  • src/components/layout/SidebarControls.test.tsx
  • src/components/layout/SidebarControls.tsx
  • src/components/layout/SidebarFolderSection.test.tsx
  • src/components/layout/SidebarFolderSection.tsx
  • src/components/notes/FolderTreeView.test.tsx
  • src/components/notes/FolderTreeView.tsx
  • src/components/notes/NoteList.tsx
  • src/lib/folderTree.test.ts
  • src/lib/folderTree.ts
  • src/services/notes.ts
  • src/test/setup.ts
  • vitest.config.ts
💤 Files with no reviewable changes (1)
  • src/components/layout/SidebarControls.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/components/layout/SidebarFolderSection.test.tsx
  • src/components/layout/Sidebar.tsx
  • vitest.config.ts
  • src/components/notes/FolderTreeView.tsx

Comment on lines +63 to +80
it("does not prevent Radix focus restoration on the sort trigger", () => {
const onChange = vi.fn();
const container = document.createElement("div");
document.body.append(container);
const root = createRoot(container);

act(() => {
root.render(
<TooltipProvider>
<NoteSortMenu sortOrder="newest" onChange={onChange} />
</TooltipProvider>,
);
});

const source = container.innerHTML;
expect(source).not.toContain("onCloseAutoFocus");

act(() => root.unmount());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Test focus restoration through behavior.

Line 77 cannot detect onCloseAutoFocus. React does not serialize event props into innerHTML. This test will pass if a future change adds that handler. Close the menu and assert that focus returns to the sort trigger.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/layout/SidebarControls.test.tsx` around lines 63 - 80, Replace
the innerHTML assertion in the “does not prevent Radix focus restoration on the
sort trigger” test with a behavioral focus-restoration check: open and close the
sort menu, then assert that focus returns to the sort trigger. Use the existing
NoteSortMenu interaction path and preserve cleanup of the rendered root.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant