fix(ui): introduce multiple scans in compliance tour

Add a dedicated scan-mode step, bump the tour version, and anchor it to the shared tabs without passing layout classes into Design System components.
This commit is contained in:
alejandrobailo
2026-07-22 13:49:41 +02:00
parent 69af0281d6
commit 3d7ddfc5a0
4 changed files with 55 additions and 31 deletions
@@ -67,6 +67,21 @@ describe("CompliancePageTabs", () => {
expect(pushMock).toHaveBeenCalledWith("/compliance");
});
it("anchors the tour to the scan-mode controls", () => {
const { container } = render(
<CompliancePageTabs
activeTab={COMPLIANCE_TAB.PER_SCAN}
crossProviderEnabled
perScanContent={<div>Per scan content</div>}
crossProviderContent={<div>Cross provider content</div>}
/>,
);
expect(
container.querySelector('[data-tour-id="view-compliance-tabs"]'),
).toBeInTheDocument();
});
it("opens the cross-provider upgrade without changing tabs in Local Server", async () => {
const user = userEvent.setup();
render(
@@ -57,32 +57,33 @@ export const CompliancePageTabs = ({
};
return (
// Same layout spacing as the scans view tabs (scans-page-shell.tsx).
<Tabs
value={activeTab}
onValueChange={handleTabChange}
className="flex flex-col gap-[18px]"
>
<TabsList className="overflow-x-auto">
<TabsTrigger value={COMPLIANCE_TAB.PER_SCAN}>Single Scan</TabsTrigger>
<TabsTrigger
value={COMPLIANCE_TAB.CROSS_PROVIDER}
adornment={
!crossProviderEnabled ? (
<Badge variant="cloud">Cloud</Badge>
) : undefined
}
>
Multiple Scans
</TabsTrigger>
</TabsList>
<Tabs value={activeTab} onValueChange={handleTabChange}>
<div className="flex flex-col gap-[18px]">
<div data-tour-id="view-compliance-tabs" className="overflow-x-auto">
<TabsList>
<TabsTrigger value={COMPLIANCE_TAB.PER_SCAN}>
Single Scan
</TabsTrigger>
<TabsTrigger
value={COMPLIANCE_TAB.CROSS_PROVIDER}
adornment={
!crossProviderEnabled ? (
<Badge variant="cloud">Cloud</Badge>
) : undefined
}
>
Multiple Scans
</TabsTrigger>
</TabsList>
</div>
<TabsContent value={COMPLIANCE_TAB.PER_SCAN}>
{perScanContent}
</TabsContent>
<TabsContent value={COMPLIANCE_TAB.CROSS_PROVIDER}>
{crossProviderContent}
</TabsContent>
<TabsContent value={COMPLIANCE_TAB.PER_SCAN}>
{perScanContent}
</TabsContent>
<TabsContent value={COMPLIANCE_TAB.CROSS_PROVIDER}>
{crossProviderContent}
</TabsContent>
</div>
</Tabs>
);
};
@@ -6,8 +6,7 @@ import {
type ViewComplianceTourTarget,
} from "../view-compliance.tour";
// Only these two carry a `data-tour-id` in the UI; welcome step has no target.
const ALLOWED_TARGETS = ["frameworks", "search"] as const;
const ALLOWED_TARGETS = ["tabs", "frameworks", "search"] as const;
const definedTargets = (): ViewComplianceTourTarget[] =>
viewComplianceTour.steps
@@ -23,14 +22,14 @@ describe("viewComplianceTour shape", () => {
it("declares a positive integer version", () => {
expect(Number.isInteger(viewComplianceTour.version)).toBe(true);
expect(viewComplianceTour.version).toBeGreaterThan(0);
expect(viewComplianceTour.version).toBe(2);
});
it("anchors exactly the search and frameworks steps, in that order", () => {
it("introduces the scan modes before search and frameworks", () => {
// Search sits above the cards in the DOM; the tour must follow top-to-bottom
// so the spotlight never jumps back up the page.
const targets = definedTargets();
expect(targets).toEqual(["search", "frameworks"]);
expect(targets).toEqual(["tabs", "search", "frameworks"]);
});
it("never targets an element outside the allowed anchor set", () => {
+10 -1
View File
@@ -7,6 +7,7 @@ import {
// Const map keeps the union narrow so `useDriverTour` can validate step keys.
export const VIEW_COMPLIANCE_TOUR_TARGETS = {
TABS: "tabs",
FRAMEWORKS: "frameworks",
SEARCH: "search",
} as const;
@@ -16,7 +17,7 @@ export type ViewComplianceTourTarget =
export const viewComplianceTour = defineTour<ViewComplianceTourTarget>({
id: "view-compliance",
version: 1,
version: 2,
coversFiles: [
"ui/app/(prowler)/compliance/**",
"ui/components/compliance/**",
@@ -27,6 +28,14 @@ export const viewComplianceTour = defineTour<ViewComplianceTourTarget>({
description:
"Compliance maps your findings to frameworks like CIS so you can see where you stand against each standard.",
},
{
target: "tabs",
side: TOUR_STEP_SIDES.BOTTOM,
align: TOUR_STEP_ALIGNMENTS.START,
title: "Choose how to combine scans",
description:
"Single Scan reviews one scan at a time. Multiple Scans combines results across provider types or across accounts of the same provider type.",
},
{
target: "search",
side: TOUR_STEP_SIDES.BOTTOM,