Skip to content

Fleet UI: Empty state followups#45039

Merged
RachelElysia merged 3 commits intomainfrom
35483-empty-states-followup
May 8, 2026
Merged

Fleet UI: Empty state followups#45039
RachelElysia merged 3 commits intomainfrom
35483-empty-states-followup

Conversation

@RachelElysia
Copy link
Copy Markdown
Member

@RachelElysia RachelElysia commented May 8, 2026

Issue

Follow ups to #35483

Description

Addresses:

Testing

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

Summary by CodeRabbit

  • Bug Fixes
    • Fixed filtered empty state message display in hosts management page
    • Corrected script batch target limit shown in tooltips
    • Updated SelectReportModal styling to use correct CSS class naming
    • Restored table header visibility in Policies card and improved empty state handling

@codecov
Copy link
Copy Markdown

codecov Bot commented May 8, 2026

Codecov Report

❌ Patch coverage is 87.50000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 66.78%. Comparing base (5a972b3) to head (7709905).
⚠️ Report is 19 commits behind head on main.

Files with missing lines Patch % Lines
...nd/pages/hosts/ManageHostsPage/ManageHostsPage.tsx 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #45039   +/-   ##
=======================================
  Coverage   66.77%   66.78%           
=======================================
  Files        2718     2718           
  Lines      218818   218806   -12     
  Branches    10748    10744    -4     
=======================================
+ Hits       146123   146136   +13     
+ Misses      59532    59506   -26     
- Partials    13163    13164    +1     
Flag Coverage Δ
frontend 55.30% <87.50%> (+0.06%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

@RachelElysia RachelElysia changed the title Fleet UI: Remove old empty state Fleet UI: Empty state followups May 8, 2026
@RachelElysia
Copy link
Copy Markdown
Member Author

@claude review

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

LGTM — small, low-risk frontend cleanup with new test coverage.

Extended reasoning...

Overview

This PR is a frontend-only follow-up to the empty-state work in #35483. It touches three host-related UI areas:

  • ManageHostsPage.tsx: removes a duplicate/now-unreachable empty-state branch and replaces a hardcoded "5,000" with MAX_SCRIPT_BATCH_TARGETS.toLocaleString().
  • SelectReportModal: renames the CSS baseClass from select-query-modal to select-report-modal (matches the SCSS file and component name), plus trivial whitespace.
  • HostPolicies.tsx: collapses the conditional "policies.length === 0" branch into the TableContainer's emptyComponent prop, and drops the now-unneeded display: none rule on .table-container__header.

Security risks

None. Frontend presentational changes with no new data flow, no auth/permissions logic, no external inputs, and no API surface.

Level of scrutiny

Low. The diff is mostly mechanical: dead-code removal, a constant substitution, a CSS classname fix, and an empty-state refactor that delegates to existing infrastructure (TableContainer.emptyComponent). The ManageHostsPage change is safe because the deleted block was made unreachable by the refactor — renderTable's own emptyState() factory already covers both isTrulyEmpty and filtered-empty cases, and the emptyComponent is wired through TableContainer.

Other factors

A new test in ManageHostsPage.tests.tsx exercises the filtered empty state via a low_disk_space=32 query param and asserts controls remain enabled, which guards the most likely regression from removing the old branch. Codecov reports 87.5% patch coverage. The bug hunting system found nothing.

@RachelElysia RachelElysia marked this pull request as ready for review May 8, 2026 19:05
@RachelElysia RachelElysia requested a review from a team as a code owner May 8, 2026 19:05
Copilot AI review requested due to automatic review settings May 8, 2026 19:05
Copy link
Copy Markdown

@claude claude Bot left a comment

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.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Review Change Stack

Walkthrough

This PR refactors empty-state handling and UI naming across multiple host management components. SelectReportModal's CSS prefix changes from select-query-modal to select-report-modal. ManageHostsPage removes the "maybe empty" state conditional, simplifying the rendering logic, and updates the Run script batch tooltip to use a configurable constant instead of a hardcoded value. HostPolicies refactors its empty-state by wiring an EmptyState component into TableContainer via the emptyComponent prop, allowing consistent table rendering. The SCSS for HostPolicies removes the header-hiding rule, and ManageHostsPage gains test coverage for the filtered empty-state scenario with query parameters.

Possibly related PRs

  • fleetdm/fleet#44880: Modifies ManageHostsPage empty-state and test logic, related through the same component's empty-state behavior refactoring.
  • fleetdm/fleet#44884: Modifies HostPolicies card empty-state wiring into TableContainer with renderCount usage, directly related to the same pattern.
  • fleetdm/fleet#44940: Updates ManageHostsPage tooltip and Run script behavior, plus SelectReportModal renaming and styling tweaks.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete. While it references the related issue and lists four specific discussion links being addressed, it does not fill out the required checklist items, lacks detailed explanation of changes, and does not include required testing confirmation or a changes file entry. Complete the testing checklist by checking relevant boxes or noting why they don't apply. Add a changes file entry and provide more detailed descriptions of the modifications made across the five files.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fleet UI: Empty state followups' is clearly related to the main changes, which focus on refactoring empty-state handling across multiple UI components in the Fleet frontend.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 35483-empty-states-followup

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tests.tsx (1)

187-220: ⚡ Quick win

Good coverage for the low_disk_space filter path

The test correctly validates that a query-param filter prevents the truly-empty state and keeps controls enabled.

Consider adding a negative assertion to make the "not truly empty" intent explicit, and asserting the export button is absent (count=0, not isTrulyEmpty → the button is intentionally hidden):

💡 Suggested additions
     expect(
       await screen.findByText("No hosts match your filters")
     ).toBeInTheDocument();
+    // Confirm we are NOT in the truly-empty state
+    expect(screen.queryByText("No hosts")).not.toBeInTheDocument();
     expect(
       screen.getByText(
         /Recently enrolled hosts will appear here after their first check-in/
       )
     ).toBeInTheDocument();

     // Controls are NOT disabled
     expect(screen.getByPlaceholderText(/search name/i)).not.toBeDisabled();
     expect(
       screen.getByRole("button", { name: /edit columns/i })
     ).not.toBeDisabled();
+    // Export button is hidden when count is 0 and not truly empty
+    expect(
+      screen.queryByRole("button", { name: /export hosts/i })
+    ).not.toBeInTheDocument();
🤖 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 `@frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tests.tsx` around lines
187 - 220, Add an explicit negative assertion in the "renders filtered empty
state for query param filters with enabled controls" test to show this is the
filtered (not truly-empty) state: after the existing checks, assert that the
export control is absent by querying for the export button/dropdown (e.g.,
queryAllByRole("button", { name: /export/i }) or queryByRole) and expecting zero
results; this makes the intent explicit instead of relying on an `isTrulyEmpty`
flag.
frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx (1)

1759-1759: 💤 Low value

LGTM — tooltip now tracks the constant

Replacing the hardcoded "5,000" with MAX_SCRIPT_BATCH_TARGETS.toLocaleString() ensures the tooltip stays in sync if the constant ever changes.

One small note: toLocaleString() with no locale argument uses the browser's default locale (e.g., "5.000" in German, "5,000" in English). Since all surrounding strings are hardcoded in English, you may want to pin the locale:

- `Target at most ${MAX_SCRIPT_BATCH_TARGETS.toLocaleString()} hosts to run a script.`
+ `Target at most ${MAX_SCRIPT_BATCH_TARGETS.toLocaleString("en-US")} hosts to run a script.`

This is only relevant if users ever run Fleet UI in a non-English locale.

🤖 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 `@frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx` at line 1759,
Tooltip message now uses MAX_SCRIPT_BATCH_TARGETS.toLocaleString() which varies
by user locale; change the call to use a pinned English locale so the tooltip
remains English-consistent (e.g., call toLocaleString with 'en-US') when setting
disableRunScriptBatchTooltipContent and any other occurrences referencing
MAX_SCRIPT_BATCH_TARGETS.toLocaleString().
🤖 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 `@frontend/pages/hosts/details/cards/Policies/HostPolicies.tsx`:
- Around line 148-153: The primaryButton currently renders the "Manage policies"
Button solely when canManagePolicies is true, which can produce a non-functional
CTA if onManagePolicies is undefined; update the conditional for primaryButton
to require both canManagePolicies and onManagePolicies before rendering the
Button (i.e., only render <Button onClick={onManagePolicies} ...> when both
canManagePolicies and onManagePolicies are truthy) so the Button's onClick
handler is always present; check the HostPolicies component's primaryButton prop
and its use of Button, canManagePolicies, and onManagePolicies to implement this
guard.

---

Nitpick comments:
In `@frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tests.tsx`:
- Around line 187-220: Add an explicit negative assertion in the "renders
filtered empty state for query param filters with enabled controls" test to show
this is the filtered (not truly-empty) state: after the existing checks, assert
that the export control is absent by querying for the export button/dropdown
(e.g., queryAllByRole("button", { name: /export/i }) or queryByRole) and
expecting zero results; this makes the intent explicit instead of relying on an
`isTrulyEmpty` flag.

In `@frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx`:
- Line 1759: Tooltip message now uses MAX_SCRIPT_BATCH_TARGETS.toLocaleString()
which varies by user locale; change the call to use a pinned English locale so
the tooltip remains English-consistent (e.g., call toLocaleString with 'en-US')
when setting disableRunScriptBatchTooltipContent and any other occurrences
referencing MAX_SCRIPT_BATCH_TARGETS.toLocaleString().
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f9fac436-01bf-4154-bd01-cf622ae651cf

📥 Commits

Reviewing files that changed from the base of the PR and between 29fd9c5 and 7709905.

📒 Files selected for processing (6)
  • frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tests.tsx
  • frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx
  • frontend/pages/hosts/details/HostDetailsPage/modals/SelectReportModal/SelectReportModal.tsx
  • frontend/pages/hosts/details/HostDetailsPage/modals/SelectReportModal/_styles.scss
  • frontend/pages/hosts/details/cards/Policies/HostPolicies.tsx
  • frontend/pages/hosts/details/cards/Policies/_styles.scss

Comment on lines +148 to +153
primaryButton={
canManagePolicies ? (
<Button onClick={onManagePolicies} type="button">
Manage policies
</Button>
) : undefined
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard the “Manage policies” CTA on callback presence.

Line 149 currently keys button rendering only on canManagePolicies. If onManagePolicies is missing, this can surface a non-functional primary action. Gate on both conditions.

Suggested fix
+    const canShowManagePoliciesCta = canManagePolicies && !!onManagePolicies;
+
     return (
       <>
         {renderBanner()}
         <TableContainer
@@
               header="No policies checked"
               info={`Select Refetch to load the latest data from ${target}${manageClause}`}
               primaryButton={
-                canManagePolicies ? (
+                canShowManagePoliciesCta ? (
                   <Button onClick={onManagePolicies} type="button">
                     Manage policies
                   </Button>
                 ) : undefined
               }
🤖 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 `@frontend/pages/hosts/details/cards/Policies/HostPolicies.tsx` around lines
148 - 153, The primaryButton currently renders the "Manage policies" Button
solely when canManagePolicies is true, which can produce a non-functional CTA if
onManagePolicies is undefined; update the conditional for primaryButton to
require both canManagePolicies and onManagePolicies before rendering the Button
(i.e., only render <Button onClick={onManagePolicies} ...> when both
canManagePolicies and onManagePolicies are truthy) so the Button's onClick
handler is always present; check the HostPolicies component's primaryButton prop
and its use of Button, canManagePolicies, and onManagePolicies to implement this
guard.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR follows up on the Fleet UI empty-state normalization work by tightening up empty-state behavior across host management and host details UI, and aligning a few implementation details with the updated patterns introduced in prior PRs.

Changes:

  • Manage hosts: remove a special-case early-return empty state so query-param filtered “empty” results still render the table chrome (controls remain enabled), and add test coverage for this scenario.
  • Host details: move the “No policies checked” empty state into the TableContainer’s emptyComponent so the empty-state behavior is consistent with other tables.
  • Fix minor UI consistency issues (e.g., SelectReportModal base CSS class naming; run-script batch tooltip now reflects MAX_SCRIPT_BATCH_TARGETS).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tsx Removes an early-return empty-state branch so filtered empty results render through TableContainer, and updates script batch tooltip copy to use the shared constant.
frontend/pages/hosts/ManageHostsPage/ManageHostsPage.tests.tsx Adds a test ensuring query-param filters (e.g., low_disk_space) show the filtered empty state while keeping controls enabled.
frontend/pages/hosts/details/HostDetailsPage/modals/SelectReportModal/SelectReportModal.tsx Renames the modal base class to match the component intent and associated styles.
frontend/pages/hosts/details/HostDetailsPage/modals/SelectReportModal/_styles.scss Minor formatting; styles target the corrected base class.
frontend/pages/hosts/details/cards/Policies/HostPolicies.tsx Consolidates the “No policies checked” empty state into TableContainer.emptyComponent to normalize behavior.
frontend/pages/hosts/details/cards/Policies/_styles.scss Removes hiding of the table header so the standard TableContainer header/count can render.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@RachelElysia RachelElysia merged commit 74c4883 into main May 8, 2026
29 checks passed
@RachelElysia RachelElysia deleted the 35483-empty-states-followup branch May 8, 2026 19:32
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