Skip to content

Fleet UI: Self service page > Custom categories - #46566

Merged
RachelElysia merged 13 commits into
feat/39018-self-service-categoriesfrom
39018-custom-categories-self-service-page
Jun 4, 2026
Merged

Fleet UI: Self service page > Custom categories#46566
RachelElysia merged 13 commits into
feat/39018-self-service-categoriesfrom
39018-custom-categories-self-service-page

Conversation

@RachelElysia

Copy link
Copy Markdown
Member

Issue

Part of #39018
Closes

Description

Screenrecording

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

@RachelElysia RachelElysia changed the title 39018 custom categories self service page Fleet UI: Self service page > Custom categories Jun 1, 2026
@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.78341% with 20 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (feat/39018-self-service-categories@9f6f80d). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...rvice/components/CategoryFilter/CategoryFilter.tsx 87.35% 11 Missing ⚠️
...perienceModal/CategoriesEndUserExperienceModal.tsx 22.22% 7 Missing ⚠️
...e/components/SelfServiceTable/SelfServiceTable.tsx 87.50% 1 Missing ⚠️
.../test/handlers/self-service-categories-handlers.ts 88.88% 1 Missing ⚠️
Additional details and impacted files
@@                          Coverage Diff                          @@
##             feat/39018-self-service-categories   #46566   +/-   ##
=====================================================================
  Coverage                                      ?   66.88%           
=====================================================================
  Files                                         ?     2812           
  Lines                                         ?   223940           
  Branches                                      ?    11431           
=====================================================================
  Hits                                          ?   149783           
  Misses                                        ?    60603           
  Partials                                      ?    13554           
Flag Coverage Δ
frontend 57.13% <90.78%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Introduces the CategoryFilter dropdown for the self-service software
page, with a sticky in-menu search input. Includes the
ISelfServiceCategory interface and self_service_categories service
entity used by the component.
Adds the InstallAllInCategoryButton component and confirmation modal,
the DEVICE_SOFTWARE_INSTALL_ALL endpoint, and the
installAllSelfServiceSoftwareInCategory service method. Posts to
/device/{token}/software/install_all with an optional category_id.
Replaces the static category sidebar on the My device > Self-service
page with a custom-category dropdown driven by the org's self-service
categories. Adds the SELF_SERVICE_CATEGORIES endpoints and MSW handlers
to mock the backend while it lands.
- Replace react-select control with a Fleet <Button variant="unstyled"> trigger so the dropdown gets the standard :focus-visible outline on Tab (and nothing on mouse click)
- Animate chevron on open/close via __icon / __icon--open modifier; move CustomDropdownIndicator to module scope so react-select doesn't unmount it mid-transition
- Keep react-select's hidden Control rendered (out of Tab order) so ArrowDown/Up/Enter/Escape forward from the in-menu search input via dispatched KeyboardEvent
- Auto-focus the in-menu search input on open; stop mousedown propagation on the input and MenuList padding so react-select's onMenuMouseDown doesn't steal focus and close the menu
- Cap trigger label and option rows at max-width 350px with ellipsis; menu max-width 350px so very long custom category names can't blow out the layout
- Type onClickInstallAction properly (was `any`) and make it required
- Add fleet arg to the categories queryKey to prevent cache bleed
- filterSoftwareByCustomCategory returns [] when categoryId is stale
- Case-insensitive category name match against software's categories enum
- Drop deprecated keyCode/which from forwarded keydown events
- Auto-clear stale category_id from URL when categories load empty
- Hide the category dropdown entirely when org has no categories
- Exclude recently_uninstalled and failed_uninstall* from install_all count
- Close install-all modal before firing the success callback
- Drop the "All pinned" filter and let the no-match message render
- Fixed dropdown width to 340px; empty-state height matches an option row
- Delete dead filterSoftwareByCategory; @deprecate stale category constants
- Style polish: remove redundant transition + transform: rotate(0deg)
- Tests: CategoryFilter.tests.tsx (new), install-all confirm flow,
  hidden-dropdown state, stale-URL recovery, realistic per_page fixture
- Replace brittle hardcoded react-select-N-listbox id lookup with
  aria-controls
…derive isEmptySearch locally (#39018)

- Exclude `ran_script` from install_all eligibility (already-run scripts
  shouldn't re-run); `never_ran_script` stays eligible so first-time
  script runs are queued
- Replace inventory-shaped EmptySoftwareTable fallback in SelfServiceTable
  with self-service-appropriate copy
- Derive `isEmptySearch` inside SelfServiceTable from `queryParams.query`
  instead of threading a drift-prone prop from SelfService.tsx (which
  was computed against the API response, not the filtered list); drop
  the prop from SelfServiceCard and SelfService.tsx
…onents (#39018)

- helpers.tests.ts: 24 tests; one per status exclusion in
  INSTALLED_OR_IN_FLIGHT_UI_STATUSES so future edits don't silently
  drop recently_uninstalled, failed_uninstall_*, ran_script, etc.
  Also covers filterSoftwareByCustomCategory's stale-URL and
  case-insensitive paths
- InstallAllInCategoryButton.tests.tsx: render states, modal confirm
  flow (POSTs with/without category_id), cancel-without-POST, error
  path keeps modal open and doesn't fire onSuccess
- InstallAllInCategoryModal.tests.tsx: title + confirm/cancel handlers,
  singular/plural copy, disabled-while-submitting
- SelfServiceFilters.tests.tsx: CategoryFilter gate (hidden when
  categories empty), install-all slot rendering
@RachelElysia
RachelElysia force-pushed the 39018-custom-categories-self-service-page branch from f86f8c1 to 83681fd Compare June 2, 2026 18:27
Replaces the left-side CategoriesMenu in SelfServicePreview with a static
"All" dropdown above the table, threads teamId through both preview
modal callsites so categories are fetched per fleet, and adds an Install
status column to the preview table. Removes the now-unused CategoriesMenu
component and CATEGORIES_NAV_ITEMS export.
@RachelElysia
RachelElysia marked this pull request as ready for review June 3, 2026 20:34
@RachelElysia
RachelElysia requested a review from a team as a code owner June 3, 2026 20:34

@claude claude 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.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@RachelElysia

Copy link
Copy Markdown
Member Author

@claude review

…-id recovery (#39018)

- Wire SelfServiceCard to GET /device/{token}/software/self_service_categories
  so categories scope by the host's fleet via the device token, instead of the
  hardcoded fleet_id=0 stand-in. Adds the endpoint constant, API method, and
  matching MSW handlers.
- Widen the auto-clear effect to also drop category_id when it's not in the
  loaded categories list (previously only fired on empty list), preventing the
  "trigger says All, table empty" state when a bookmarked id has been deleted.
- Remove a duplicate :has() rule in CategoriesEndUserExperienceModal styles.
… renders (#39018)

Restructures SelfServiceFilters into flat siblings (CategoryFilter,
__install-all, __search) and adds a --with-categories modifier on the
filter row. Under $break-sm, that modifier scopes a media query that
sends __search to its own full-width top row, leaving CategoryFilter
(left) and Install all (right) on row 2. Without categories the row
stays single-line at any width.
@RachelElysia

Copy link
Copy Markdown
Member Author

TODO: Ensure success/error messages are what we want (missing in specs)
TODO: test keyboard accessibility

@RachelElysia
RachelElysia merged commit 1036719 into feat/39018-self-service-categories Jun 4, 2026
17 checks passed
@RachelElysia
RachelElysia deleted the 39018-custom-categories-self-service-page branch June 4, 2026 16:59
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.

2 participants