Fleet UI: Add add variable button, update no results live empty state (#45808)

This commit is contained in:
RachelElysia
2026-05-19 12:18:53 -04:00
committed by GitHub
parent 75295ae163
commit fdc101e306
5 changed files with 83 additions and 72 deletions
+3
View File
@@ -0,0 +1,3 @@
- UI table controls stay visible when empty. On Hosts, Reports, Policies, and Software pages, search bars, filters, dropdowns, remain visible but disabled when empty — avoids layout shift when the first item is added. Item count remains visible.
- UI has action-oriented empty state copy. Headers describe current state ("No hosts", "No policies for this fleet") instead of prompting action. Body text explains what to expect. CTA buttons are explicit ("Add policy", "Schedule a report") and permission gated.
- UI shows consistent page descriptions and learn more links (added to Settings. Fleets, Ticket destinations, Certificates, and Identity provider pages).
@@ -64,9 +64,11 @@ describe("Custom variables", () => {
"Add a custom variable to make it available in scripts and profiles."
)
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: "Add custom variable" })
).toBeInTheDocument();
// Header button and EmptyState CTA button both render
const addButtons = screen.getAllByRole("button", {
name: /Add custom variable/,
});
expect(addButtons).toHaveLength(2);
});
});
@@ -167,43 +167,19 @@ const Variables = () => {
</>
);
const renderPageDescription = () => (
<PageDescription
variant="tab-panel"
content={
<>
{isPremiumTier
? "Manage custom variables that will be available in scripts and profiles across all fleets."
: "Manage custom variables that will be available in scripts and profiles."}{" "}
<CustomLink
text="Learn more"
url={`${FLEET_WEBSITE_URL}/guides/secrets-in-scripts-and-configuration-profiles`}
newTab
/>
</>
}
/>
);
const isEmpty = !isLoading && data?.count === 0;
if (isLoading) {
return (
<div className={baseClass}>
<div className={`${baseClass}__page-header`}>
{renderPageDescription()}
</div>
const renderContent = () => {
if (isLoading) {
return (
<div className={`${baseClass}__loading`}>
<Spinner />
</div>
</div>
);
}
);
}
if (data?.count === 0) {
return (
<div className={baseClass}>
<div className={`${baseClass}__page-header`}>
{renderPageDescription()}
</div>
if (isEmpty) {
return (
<EmptyState
variant="header-list"
header="No custom variables"
@@ -227,36 +203,10 @@ const Variables = () => {
) : undefined
}
/>
{showAddModal && (
<AddCustomVariableModal
onCancel={() => setShowAddModal(false)}
onSave={onSaveVariable}
/>
)}
</div>
);
}
);
}
return (
<div className={baseClass}>
<div className={`${baseClass}__page-header`}>
{renderPageDescription()}
{canEdit && (
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
variant="inverse"
size="small"
onClick={onClickAddVariable}
disabled={disableChildren}
>
<Icon name="plus" />
<span>Add custom variable</span>
</Button>
)}
/>
)}
</div>
return (
<PaginatedList<IVariable>
ref={paginatedListRef}
pageSize={VARIABLES_PAGE_SIZE}
@@ -281,6 +231,44 @@ const Variables = () => {
</span>
}
/>
);
};
return (
<div className={baseClass}>
<div className={`${baseClass}__page-header`}>
<PageDescription
variant="tab-panel"
content={
<>
{isPremiumTier
? "Manage custom variables that will be available in scripts and profiles across all fleets."
: "Manage custom variables that will be available in scripts and profiles."}{" "}
<CustomLink
text="Learn more"
url={`${FLEET_WEBSITE_URL}/guides/secrets-in-scripts-and-configuration-profiles`}
newTab
/>
</>
}
/>
{canEdit && (
<GitOpsModeTooltipWrapper
renderChildren={(disableChildren) => (
<Button
variant="inverse"
size="small"
onClick={onClickAddVariable}
disabled={disableChildren}
>
<Icon name="plus" />
<span>Add custom variable</span>
</Button>
)}
/>
)}
</div>
{renderContent()}
{showAddModal && (
<AddCustomVariableModal
onCancel={() => setShowAddModal(false)}
@@ -165,12 +165,21 @@ const PolicyResults = ({
}
if (finishedWithNoResults) {
const hostVerb = targetsTotalCount === 1 ? "host is" : "hosts are";
const errorsMessage = errors?.length ? (
<>
{" "}
or review the <strong>Errors</strong> tab for details
</>
) : null;
return (
<EmptyState
header="Your live report returned no results"
info={`Expecting to see results? Check to see if the host${
targetsTotalCount > 1 ? "s" : ""
} you targeted reported "Online" or check out the "Errors" table.`}
header="No results returned"
info={
<>
Check whether the {hostVerb} online{errorsMessage}.
</>
}
/>
);
}
@@ -156,12 +156,21 @@ const QueryResults = ({
};
const renderNoResults = () => {
const hostVerb = targetsTotalCount === 1 ? "host is" : "hosts are";
const errorsMessage = errors?.length ? (
<>
{" "}
or review the <strong>Errors</strong> tab for details
</>
) : null;
return (
<EmptyState
header="Your live report returned no results"
info={`Expecting to see results? Check to see if the host${
targetsTotalCount > 1 ? "s" : ""
} you targeted reported "Online" or check out the "Errors" table.`}
header="No results returned"
info={
<>
Check whether the {hostVerb} online{errorsMessage}.
</>
}
/>
);
};