fix(ui): align scan filters and actions (#12781)

This commit is contained in:
Alejandro Bailo
2026-09-09 23:05:09 +02:00
committed by GitHub
parent 369f852837
commit 8270979ec8
10 changed files with 73 additions and 61 deletions
@@ -0,0 +1 @@
Scans page filter widths and action button styling, with Launch Scan and Import Findings grouped beside the tabs and sized consistently with Configure Mutelist
@@ -223,10 +223,9 @@ export function ImportFindingsModal() {
<>
<Button
type="button"
size="lg"
variant="secondary"
variant="outline"
onClick={() => handleOpenChange(true)}
className="w-full md:w-auto"
className="w-full sm:w-auto"
>
Import Findings
</Button>
+29 -25
View File
@@ -31,7 +31,7 @@ interface ScansFilterBarProps {
onScanStatusChange: (value: string) => void;
}
const filterItemClass = "w-full md:w-[calc(50%-0.375rem)] xl:w-60";
const filterItemClass = "w-full sm:max-w-[240px] sm:min-w-[180px] sm:flex-1";
export function ScansFilterBar({
providers,
@@ -67,33 +67,37 @@ export function ScansFilterBar({
</div>
{showScheduleTypeFilter && (
<Select value={scheduleType} onValueChange={onScheduleTypeChange}>
<SelectTrigger aria-label="All Types" className={filterItemClass}>
<SelectValue placeholder="All Types" />
</SelectTrigger>
<SelectContent>
{triggerFilterOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
<div className={filterItemClass}>
<Select value={scheduleType} onValueChange={onScheduleTypeChange}>
<SelectTrigger aria-label="All Types">
<SelectValue placeholder="All Types" />
</SelectTrigger>
<SelectContent>
{triggerFilterOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
{showStatusFilter && (
<Select value={scanStatus} onValueChange={onScanStatusChange}>
<SelectTrigger aria-label="All Statuses" className={filterItemClass}>
<SelectValue placeholder="All Statuses" />
</SelectTrigger>
<SelectContent>
{statusFilterOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
<div className={filterItemClass}>
<Select value={scanStatus} onValueChange={onScanStatusChange}>
<SelectTrigger aria-label="All Statuses">
<SelectValue placeholder="All Statuses" />
</SelectTrigger>
<SelectContent>
{statusFilterOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
</>
);
+20 -11
View File
@@ -336,26 +336,35 @@ describe("ScansPageShell", () => {
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
});
it("keeps launch scan with filters and mutelist with tabs", () => {
vi.stubEnv("UI_CLOUD_ENABLED", "false");
it("keeps launch scan, import findings, and mutelist with tabs", () => {
// Given
vi.stubEnv("UI_CLOUD_ENABLED", "true");
// When
render(
<ScansPageShell providers={providers} hasManageScansPermission>
<ScansPageShell
providers={providers}
hasManageScansPermission
hasManageIngestionsPermission
>
<div>Scans table</div>
</ScansPageShell>,
);
expect(
screen.getByRole("group", { name: /scan filters and actions/i }),
).toContainElement(screen.getByRole("button", { name: /launch scan/i }));
expect(
screen.getByRole("group", { name: /scan filters and actions/i }),
).not.toContainElement(
screen.getByRole("link", { name: /configure mutelist/i }),
// Then
const tabs = screen.getByRole("group", { name: /scan tabs/i });
expect(tabs).toContainElement(
screen.getByRole("button", { name: /launch scan/i }),
);
expect(screen.getByRole("group", { name: /scan tabs/i })).toContainElement(
expect(tabs).toContainElement(
screen.getByRole("button", { name: /import findings/i }),
);
expect(tabs).toContainElement(
screen.getByRole("link", { name: /configure mutelist/i }),
);
expect(
screen.getByRole("group", { name: /scan filters/i }),
).toContainElement(screen.getByText("Shared scan filters"));
});
it("shows the active scans count in the in progress tab", () => {
+17 -19
View File
@@ -135,8 +135,8 @@ export function ScansPageShell({
)}
<div
role="group"
aria-label="Scan filters and actions"
className="flex flex-wrap items-center gap-3"
aria-label="Scan filters"
className="flex flex-wrap items-center gap-4"
>
<ScansFilterBar
providers={providers}
@@ -148,20 +148,6 @@ export function ScansPageShell({
onScheduleTypeChange={filters.setScheduleType}
onScanStatusChange={filters.setScanStatus}
/>
<Button
type="button"
size="lg"
onClick={() => handleLaunchOpenChange(true)}
disabled={launchDisabled}
className="w-full md:w-auto"
data-tour-id="view-first-scan-launch"
>
Launch Scan
</Button>
{isCloudEnvironment && hasManageIngestionsPermission && (
<ImportFindingsModal />
)}
</div>
{isCloudEnvironment && <CliImportBanner />}
@@ -174,10 +160,10 @@ export function ScansPageShell({
<div
role="group"
aria-label="Scan tabs"
className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"
className="flex flex-wrap items-center justify-between gap-3"
>
<TabsList
className="overflow-x-auto"
className="w-full overflow-x-auto sm:w-auto"
data-tour-id="view-first-scan-tabs"
>
{Object.values(SCAN_JOBS_TAB).map((tab) => (
@@ -186,7 +172,19 @@ export function ScansPageShell({
</TabsTrigger>
))}
</TabsList>
<div className="shrink-0">
<div className="ml-auto flex w-full flex-wrap items-center gap-3 sm:w-auto">
<Button
type="button"
onClick={() => handleLaunchOpenChange(true)}
disabled={launchDisabled}
className="w-full sm:w-auto"
data-tour-id="view-first-scan-launch"
>
Launch Scan
</Button>
{isCloudEnvironment && hasManageIngestionsPermission && (
<ImportFindingsModal />
)}
<MutedFindingsConfigButton />
</div>
</div>
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

+2 -2
View File
@@ -27,10 +27,10 @@ export class ScansPage extends BasePage {
// The sidebar exposes its own icon-button labeled "Launch Scan"
// (aria-label, wrapped in a Tooltip), so scoping by accessible name
// alone hits a strict-mode duplicate. Scope to the page-shell's
// filters-and-actions group, which only contains the visible-text
// tabs-and-actions group, which only contains the visible-text
// Launch Scan button.
this.launchScanButton = page
.getByRole("group", { name: /scan filters and actions/i })
.getByRole("group", { name: /scan tabs/i })
.getByRole("button", { name: /^Launch Scan$/i });
this.launchScanDialog = page.getByRole("dialog");
// The modal renders the providers picker as the shared MultiSelect-based
+2 -1
View File
@@ -27,7 +27,7 @@
### Flow Steps
1. Navigate to Scans page
2. Click "Launch Scan" to open the launch scan modal
2. Click "Launch Scan" beside the scan tabs to open the launch scan modal
3. Open the Cloud Account selector and choose the entry whose text contains E2E_AWS_PROVIDER_ACCOUNT_ID
4. Optionally fill Scan Note
5. Click "Launch Scan" in the modal
@@ -46,6 +46,7 @@
- Scans page loads correctly
- Launch Scan modal opens correctly
- The page-level "Launch Scan" button is located in the "Scan tabs" group, distinct from the sidebar action
- Cloud Account select is available and lists the configured provider UID
- "Launch Scan" button is rendered and enabled when form is valid
- Success toast message: "The scan was launched successfully."