Skip to content

Conversation

ronjalh
Copy link
Member

@ronjalh ronjalh commented Oct 30, 2026

Summary

The sort button on the Elements page now works. It opens a menu where the user chooses the field (Code, Difficulty, Name) and the direction (Ascending, Descending), and the choice is sent to the API as sort: CatalogSort.

Closes #128

What is in it

  • SortMenu: the Base UI Menu with two labelled radio groups ("Sort by" and "Direction"). This is the menu button pattern of the WAI-ARIA Authoring Practices: the arrow keys, Enter, Space, Escape and type-ahead work, items have the role menuitemradio, and the chosen one is aria-checked. The menu stays open after a choice, so field and direction can be chosen one after the other. The button still reads "Sort by Code · Ascending" and shows the current choice.
  • The address: ?sort=difficulty&dir=desc, next to ?search=. The default (code, ascending) is left out, so the address is clean and the first request is the same as before. An unknown value is ignored. Changing the search keeps the sort and the other way round (before, the search overwrote the whole query string).
  • First page: sort and filter are part of the cache key of searchCatalog (already so in cache.ts), so a new sort is a new list that starts at page one. "Load more" is sent with the same sort.
  • Difficulty needs an apparatus (the API answers BAD_USER_INPUT otherwise): the item is disabled but can still be reached with the keyboard, and the reason is read with it (aria-describedby). ?sort=difficulty without an apparatus sorts by code and is not sent.
  • Easy to extend: the fields come from one list (SORT_FIELDS in searchOperations.ts), with a needsApparatus flag. A new field is one line there (and in the API); the menu and the address are built from the list. The sorting itself is on the server with an index and a cursor per sort, so it does not depend on how many elements are loaded.

Choices

  • The apparatus filter is not connected yet (it is still drawn only), so nothing sets an apparatus today and Difficulty cannot be chosen in the app yet. I read it from ?apparatus=<id> and send it as filter.apparatusId, so the rule works and is tested now, and the filter issue only has to write that parameter. If that issue chooses another name for the parameter, apparatusId in ElementsPage.tsx is the one place to change.
  • Base UI Menu and not a native <select>: a <select> cannot have two groups with a disabled item that explains itself, and the project already uses Base UI.

Testing

  • ElementsPage.test.tsx (11 new tests): each field and direction is sent and shown on the button; the address, the clean default, an unknown address, the search and the sort keeping each other; a new sort starts from the first page and "Load more" keeps the sort; Difficulty is disabled with the reason without an apparatus, and works with one (sends filter and sort).
  • tsc, eslint, vite build and the 250 frontend tests pass. The backend tests in this worktree could not load the Prisma client (not generated here); the backend is not changed.

🤖 Generated with Claude Code

@ronjalh
Copy link
Member Author

ronjalh commented Oct 30, 2026

Superseded by #135, which contains this sort together with the filters from #127 in one pull request (SortMenu.tsx is identical). #135 closes both #127 and #128, so this one is closed without merging.

Comment made with Claude Code in discussion with Ronja

@ronjalh ronjalh closed this Oct 30, 2026
Sign in to join this conversation on GitHub.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant