Skip to content

Fleet UI: Multi-package secondary UI — policy automation, setup experience, install-details hash#49079

Open
RachelElysia wants to merge 4 commits into
feat/28108-multiple-custom-packagesfrom
48401-multi-package-secondary-ui
Open

Fleet UI: Multi-package secondary UI — policy automation, setup experience, install-details hash#49079
RachelElysia wants to merge 4 commits into
feat/28108-multiple-custom-packagesfrom
48401-multi-package-secondary-ui

Conversation

@RachelElysia

@RachelElysia RachelElysia commented Jul 9, 2026

Copy link
Copy Markdown
Member

Issue

Closes #48401

Description

Frontend for the multi-package secondary UI. Consumes #48708's policy/setup backend and the API contract from #48607. Targets feat/28108-multiple-custom-packages.

  • Policy automation (Manage automations modal + edit-policy page): the outer "Select software" dropdown help text now reads platform (type) • <version> for single-package titles and platform (type) • N versions for multi-package titles. When a multi-package title is picked, a second "Select package" dropdown surfaces below with each package's filename + version • Added X ago. First-added (smallest installer_id) auto-selects; user can override. Payload carries software_installer_id. Single-package titles and VPP/App Store titles don't surface the second dropdown. Per Figma dev note (node 5856:36029).
  • Setup experience: version cell wraps in a TooltipWrapper with "For custom packages, the first added version will be installed." (verbatim from tooltip node 5857:5090) — only when the title has packages.length > 1. Existing labels-don't-apply-during-setup footer untouched.
  • Install-details modal: Package SHA-256 hash: DataSet row + copy button, guarded on the install-result payload's hash_sha256. Per node 5849:10905. Shipped inert against today's backend — row lights up automatically when BE hydrates the field on HostSoftwareInstallerResult (server/fleet/software_installer.go:439-487).
  • DropdownWrapper: added optional ariaLabel prop that forwards to react-select's aria-label (used by the new Select-package dropdown, backward-compatible for existing call sites).
  • Backend contract: FE always sends software_installer_id alongside software_title_id; null resolves to backend default (first-added or VPP) per server/service/team_policies.go:697-716. VPP/App Store paths save cleanly because validation gates on selectedTitlePackages?.length > 0.

Screenrecording

  • Policy automation modal with a multi-package title (dropdown behavior)
  • Setup experience install-software table (tooltip on version cell)
  • Install-details modal on a package-backed install (Hash row + copy)
Screen.Recording.2026-07-09.at.3.33.16.PM.mov
Screenshot 2026-07-09 at 3 36 11 PM

Testing

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

Test coverage:

  • PolicyAutomationsFields.tests.tsx (new) — 6 tests covering dropdown visibility across single/multi/VPP titles and payload shape (multi-package auto-selects first-added; VPP saves without error — must-fix regression guard).
  • SoftwareInstallDetailsModal.tests.tsx — 2 new tests for Hash row presence/absence based on hash_sha256.
  • helpers.tests.tsx — 2 new tests for the outer-dropdown N versions and single-version branches of generateSoftwareOptionHelpText.

…ience, install-details hash

- Policy automation (Manage automations + edit-policy): outer "Select
  software" dropdown shows "N versions" for multi-package titles and the
  version string otherwise; a second "Select package" dropdown surfaces
  for multi-package titles with per-package filename + version + upload
  date, auto-selecting first-added (smallest installer_id). Payload
  carries software_installer_id.
- Setup experience: version cell wraps in a tooltip for multi-package
  titles ("For custom packages, the first added version will be
  installed."). Single-package / VPP titles render plain version.
- Install-details modal: `Package SHA-256 hash:` DataSet row + copy
  button, guarded on the install-result payload's hash_sha256.
- DropdownWrapper: optional `ariaLabel` prop forwarded to react-select
  as `aria-label` — react-select doesn't infer one from placeholder, so
  screen readers hear "combobox" with no context otherwise.
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.04348% with 11 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (feat/28108-multiple-custom-packages@70a3206). Learn more about missing BASE report.

Files with missing lines Patch % Lines
...olicyAutomationsFields/PolicyAutomationsFields.tsx 79.06% 8 Missing and 1 partial ⚠️
server/service/software_titles.go 71.42% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@                          Coverage Diff                           @@
##             feat/28108-multiple-custom-packages   #49079   +/-   ##
======================================================================
  Coverage                                       ?   68.16%           
======================================================================
  Files                                          ?     3683           
  Lines                                          ?   234650           
  Branches                                       ?    12552           
======================================================================
  Hits                                           ?   159960           
  Misses                                         ?    60335           
  Partials                                       ?    14355           
Flag Coverage Δ
backend 69.75% <92.30%> (?)
frontend 59.51% <86.36%> (?)

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.

Backend:
- SoftwarePackageListItem: expose installer_id + uploaded_at on the list
  response so the Select-package dropdown can auto-select first-added and
  render the "Added X ago" secondary line. GetSoftwarePackagesForTitles
  SELECTs si.id and si.uploaded_at.
- AutomaticInstallPolicy: add InstallerID (nullable, json:"-") join key.
  getPoliciesBySoftwareTitleIDs SELECTs p.software_installer_id.
- software_titles list: dispatch automatic-install policies per-installer
  instead of aggregating title-level so each package on a multi-package
  title only surfaces the policies actually bound to it. VPP-backed
  policies (nil InstallerID) still flow to AppStoreApp.
- Tests: assert InstallerID is populated on custom-backed policies and
  nil on VPP-backed policies.

Frontend:
- Setup experience Version column: moved the tooltip from per-row cell
  to the column header ("For custom packages, the first added version
  will be installed.") — one hover target for the whole column, matches
  Figma tooltip node 5857:5090. Required an explicit `id: "version"` on
  the column config since Header is now a component.
- LibraryItemAccordion: GitOps-lock the trash button on custom
  multi-package titles too, matching the Edit modal's visible-but-disabled
  treatment. Single-package custom titles keep the shipped delete flow.
- generateSoftwarePackageOptionHelpText: drop the redundant "Added "
  prefix (addedFromNow already prepends it) so options read "1.2.3 •
  Added 3 days ago" instead of "1.2.3 • Added Added 3 days ago".
- __software-pickers: flex-direction: row + gap $pad-small so the two
  dropdowns sit side-by-side. __row-trailing: max-width: 60% (not width)
  so the cell doesn't force width when trailing content is short.
…detail endpoint

The list endpoint's aggregation bug had a twin on the title detail
endpoint (server/service/software_titles.go). titleMeta.AutomaticInstallPolicies
was being copied onto every package, so a multi-package title's detail
response returned the same policies on all packages regardless of which
one was actually bound.

Fix: build a policiesByInstaller map from titleMeta's list (each policy
carries an InstallerID join key from the prior fix) and dispatch
per-package. VPP-backed policies (nil InstallerID) continue to flow
via the AppStoreApp branch below.
@RachelElysia RachelElysia marked this pull request as ready for review July 9, 2026 22:39
@RachelElysia RachelElysia requested review from a team as code owners July 9, 2026 22:39
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.

3 participants