Skip to content

feat(projects): favourites, recently opened and search in the project list and switcher - #442

Merged
pashidlos merged 6 commits into
masterfrom
feat/project-list-favourites
Oct 24, 2026
Merged

pashidlos merged 6 commits into
masterfrom
feat/project-list-favourites

Conversation

@pashidlos

@pashidlos pashidlos commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

What and why

A long project list was one unsorted grid, and the switcher on the build page a plain select: in a company with many projects you had to hunt for yours every time.

Depends on Visual-Regression-Tracker/backend#432 (merged). Without it the list still works (a 404 from GET /projects/favorites counts as no favourites, with no error on login), but starring answers 404.

Project list

  • Favourites: a star on each card; starred projects are listed first under "Favourites", kept per user by the API.
  • Recently opened: the last five projects opened come next, kept in this browser per user (recentProjects:<userId>).
  • The rest follow by name. The API returns projects unordered, so the list used to come in database order.
  • Search by name, id or main branch; the query stays in the URL (?q=) and / focuses the field. With no match it says so.
  • The star shows at once and goes back, with the error, if saving it fails.
  • Builds and Variations are router links — a plain href used to reload the whole app on every click.

Switcher (build, variations and profile pages)

An autocomplete with the same groups and search; type and pick with the mouse or the keyboard. Its label id is now project-select-label (the e2e alignment test follows). It sits exactly where the select did: label, name and underline match master to the pixel row; only glyph antialiasing differs, as the name is now drawn by an input.

Also in this PR

  • eslint-plugin-sonarjs in npm run lint, so SonarCloud's findings fail locally and in CI before a PR opens. The 21 findings in the code are fixed — among them window.open for "Open an issue in GitHub" now passes noopener,noreferrer — except todo-tag (a warning) and, in Playwright specs only, assertions-in-tests and void-use (reasons in the config).
  • ProjectCard, ProjectGrid and ProjectSearchField keep the page under 300 lines.
  • Coverage floors raised to the reached level (lines 75).
  • SonarCloud's findings on this PR are fixed; require-await (its S7503, not part of eslint-plugin-sonarjs) runs locally as a warning, as older tests and mocks have 40 such functions.

How it was checked

  • npm run lint (0 errors) and npm run typecheck
  • npm test: 662 pass. Each new behaviour started with a failing test (service, helpers, context, page, switcher); a switcher crash when the first pick turned into a recent project was caught by an existing profile test and fixed.
  • npm run test:pw:docker: 324 pass, the whole visual contract included. New specs: starring and unstarring (PUT/DELETE), favourites first, search and ?q=, /, switching project by typing, and opening a project's builds without a page reload. The sign-in fixture answers no favourites by default.
    • The old per-spec renders tests of six pages screenshotted before the lazy route had rendered: they flaked, on master too, and an update run wrote the empty page as the baseline. They now wait for the page's heading and for the network to settle (utils/waitForPage.ts); the whole suite passes without retries (330/330).
  • Screenshot baselines regenerated in Docker; every changed PNG looked at. New: the list with favourites and recently opened, a search without a match, the open switcher (light and dark). Changed: the star and search on the project list; on the build and profile pages only antialiasing of the switcher's text. Baselines within the 100px tolerance of master were put back to master's. One regenerated baseline captured before the page rendered (user-list-page.png) was thrown away and kept as it was.

Screenshots

integration_tests/test/screens.spec.ts-snapshots/project-list-favourites-*.png, project-list-no-match-*.png, project-switcher-*.png.

SonarCloud only looks at a PR once it is open, so its findings arrived
after review (#438 → #439). eslint-plugin-sonarjs 4.2.2 runs the same rules
in `npm run lint`, the pre-commit hook and CI.

The 21 findings in the current code:
- window.open for "Open an issue in GitHub" now passes noopener,noreferrer,
  so the opened page gets no handle on the app's tab (test first)
- three nested ternaries and two one-case switch reducers rewritten as
  plain ifs; a float constant compared with toBeCloseTo
- todo-tag is a warning: the TODOs are debt to see, not a failed build
- off for Playwright specs only: assertions-in-tests (specs assert through
  helpers and page objects) and void-use (`void fixture` asks for a
  fixture used only for its setup)
… list and switcher

A long project list was one unsorted grid, and the switcher on the build page
a plain select. Now:

- a star on each card marks a favourite; favourites are kept per user by the
  API (GET /projects/favorites, PUT/DELETE /projects/:id/favorite) and listed
  first, under "Favourites"
- the last five projects opened are listed next, under "Recently opened",
  kept in this browser per user
- the rest follow by name; the order is the same in the list and switcher
- a search field filters by name, id or main branch, keeps the query in the
  URL (?q=) and takes focus on "/"
- the switcher on the build, variations and profile pages is an autocomplete
  with the same groups and search
- Builds and Variations are router links: they used to reload the whole app

An API without favourites (older backend) answers 404: the list then works
without them instead of greeting every login with an error. The star goes
back and the error shows if saving it fails.

ProjectCard, ProjectGrid and ProjectSearchField keep the page under 300 lines.
The e2e sign-in fixture answers no favourites by default; new specs cover
starring, the order, search, "/", the switcher and navigation without a
reload. screens.spec gains the grouped list, a search without matches and
the open switcher; baselines regenerated in Docker.
# Conflicts:
#	integration_tests/test/projectList.spec.ts
…avourites

# Conflicts:
#	integration_tests/test/projectList.spec.ts
…loud on #442; wait before legacy screenshots

- The project switcher had a margin above its label, which the select it
  replaced had not: the label, the name and everything under them sat 8px
  lower on the build, variations and profile pages. Only the sides and the
  bottom keep the inset now, and the layout matches master again.
- SonarCloud: the two favourite mocks drop an async they never awaited, and
  the lint config's comment no longer reads as an open task. require-await
  (Sonar's S7503, outside eslint-plugin-sonarjs) runs as a warning, since
  older tests and mocks have 40 such functions.
- The per-spec "renders" tests of six pages took their screenshot before the
  lazy route had rendered, so they flaked (on master too) and an update run
  wrote the empty page as the baseline. They wait for the page's heading and
  for the network to settle first.

Baselines regenerated in Docker; those within the 100px tolerance of master
are master's again.
@sonarqubecloud

Copy link
Copy Markdown

@pashidlos
pashidlos merged commit 37e30ee into master Oct 24, 2026
3 checks passed
@pashidlos
pashidlos deleted the feat/project-list-favourites branch October 24, 2026 15:53
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