mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
refactor(ui): simplify provider wizard footer state
This commit is contained in:
@@ -271,7 +271,7 @@ describe("useProviderWizardController", () => {
|
||||
expect(refreshMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not rerender when setting a semantically unchanged footer config", () => {
|
||||
it("keeps the footer setter stable and uses replacement action callbacks", () => {
|
||||
// Given
|
||||
const onOpenChange = vi.fn();
|
||||
const firstOnBack = vi.fn();
|
||||
@@ -280,15 +280,13 @@ describe("useProviderWizardController", () => {
|
||||
const latestOnBack = vi.fn();
|
||||
const latestOnSecondaryAction = vi.fn();
|
||||
const latestOnAction = vi.fn();
|
||||
let renderCount = 0;
|
||||
const { result } = renderHook(() => {
|
||||
renderCount += 1;
|
||||
|
||||
return useProviderWizardController({
|
||||
const { result } = renderHook(() =>
|
||||
useProviderWizardController({
|
||||
open: true,
|
||||
onOpenChange,
|
||||
});
|
||||
});
|
||||
}),
|
||||
);
|
||||
const initialSetFooterConfig = result.current.setFooterConfig;
|
||||
|
||||
const firstFooterConfig = {
|
||||
showBack: true,
|
||||
@@ -309,7 +307,6 @@ describe("useProviderWizardController", () => {
|
||||
act(() => {
|
||||
result.current.setFooterConfig(firstFooterConfig);
|
||||
});
|
||||
const renderCountAfterFirstUpdate = renderCount;
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
@@ -319,14 +316,15 @@ describe("useProviderWizardController", () => {
|
||||
onSecondaryAction: latestOnSecondaryAction,
|
||||
onAction: latestOnAction,
|
||||
});
|
||||
|
||||
});
|
||||
act(() => {
|
||||
result.current.resolvedFooterConfig.onBack?.();
|
||||
result.current.resolvedFooterConfig.onSecondaryAction?.();
|
||||
result.current.resolvedFooterConfig.onAction?.();
|
||||
});
|
||||
|
||||
// Then
|
||||
expect(renderCount).toBe(renderCountAfterFirstUpdate);
|
||||
expect(result.current.setFooterConfig).toBe(initialSetFooterConfig);
|
||||
expect(result.current.resolvedFooterConfig).toMatchObject({
|
||||
showBack: true,
|
||||
backLabel: "Back",
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
type Dispatch,
|
||||
type SetStateAction,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { DOCS_URLS, getProviderHelpText } from "@/lib/external-urls";
|
||||
import { isCloud } from "@/lib/shared/env";
|
||||
@@ -54,32 +48,6 @@ const EMPTY_FOOTER_CONFIG: WizardFooterConfig = {
|
||||
actionType: WIZARD_FOOTER_ACTION_TYPE.BUTTON,
|
||||
};
|
||||
|
||||
function isSameFooterConfig(
|
||||
current: WizardFooterConfig,
|
||||
next: WizardFooterConfig,
|
||||
) {
|
||||
return (
|
||||
current.showBack === next.showBack &&
|
||||
current.backLabel === next.backLabel &&
|
||||
Boolean(current.backDisabled) === Boolean(next.backDisabled) &&
|
||||
Boolean(current.showSecondaryAction) ===
|
||||
Boolean(next.showSecondaryAction) &&
|
||||
(current.secondaryActionLabel ?? "") ===
|
||||
(next.secondaryActionLabel ?? "") &&
|
||||
Boolean(current.secondaryActionDisabled) ===
|
||||
Boolean(next.secondaryActionDisabled) &&
|
||||
current.secondaryActionVariant === next.secondaryActionVariant &&
|
||||
current.secondaryActionType === next.secondaryActionType &&
|
||||
(current.secondaryActionFormId ?? "") ===
|
||||
(next.secondaryActionFormId ?? "") &&
|
||||
current.showAction === next.showAction &&
|
||||
current.actionLabel === next.actionLabel &&
|
||||
Boolean(current.actionDisabled) === Boolean(next.actionDisabled) &&
|
||||
current.actionType === next.actionType &&
|
||||
(current.actionFormId ?? "") === (next.actionFormId ?? "")
|
||||
);
|
||||
}
|
||||
|
||||
interface UseProviderWizardControllerProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
@@ -114,14 +82,8 @@ export function useProviderWizardController({
|
||||
const [orgCurrentStep, setOrgCurrentStep] = useState<OrgWizardStep>(
|
||||
ORG_WIZARD_STEP.SETUP,
|
||||
);
|
||||
const [footerConfig, setFooterConfigState] =
|
||||
const [resolvedFooterConfig, setFooterConfig] =
|
||||
useState<WizardFooterConfig>(EMPTY_FOOTER_CONFIG);
|
||||
const footerConfigRef = useRef<WizardFooterConfig>(EMPTY_FOOTER_CONFIG);
|
||||
const footerActionCallbacksRef = useRef({
|
||||
onBack: () => footerConfigRef.current.onBack?.(),
|
||||
onSecondaryAction: () => footerConfigRef.current.onSecondaryAction?.(),
|
||||
onAction: () => footerConfigRef.current.onAction?.(),
|
||||
});
|
||||
const [providerTypeHint, setProviderTypeHint] = useState<ProviderType | null>(
|
||||
null,
|
||||
);
|
||||
@@ -161,8 +123,7 @@ export function useProviderWizardController({
|
||||
);
|
||||
setOrgCurrentStep(orgInitialData.targetStep);
|
||||
setOrgSetupPhase(orgInitialData.targetPhase);
|
||||
footerConfigRef.current = EMPTY_FOOTER_CONFIG;
|
||||
setFooterConfigState(EMPTY_FOOTER_CONFIG);
|
||||
setFooterConfig(EMPTY_FOOTER_CONFIG);
|
||||
setProviderTypeHint(null);
|
||||
return;
|
||||
}
|
||||
@@ -185,8 +146,7 @@ export function useProviderWizardController({
|
||||
);
|
||||
setCurrentStep(PROVIDER_WIZARD_STEP.CREDENTIALS);
|
||||
setOrgCurrentStep(ORG_WIZARD_STEP.SETUP);
|
||||
footerConfigRef.current = EMPTY_FOOTER_CONFIG;
|
||||
setFooterConfigState(EMPTY_FOOTER_CONFIG);
|
||||
setFooterConfig(EMPTY_FOOTER_CONFIG);
|
||||
setProviderTypeHint(initialProviderType);
|
||||
setOrgSetupPhase(ORG_SETUP_PHASE.DETAILS);
|
||||
return;
|
||||
@@ -197,8 +157,7 @@ export function useProviderWizardController({
|
||||
setWizardVariant(WIZARD_VARIANT.PROVIDER);
|
||||
setCurrentStep(PROVIDER_WIZARD_STEP.CONNECT);
|
||||
setOrgCurrentStep(ORG_WIZARD_STEP.SETUP);
|
||||
footerConfigRef.current = EMPTY_FOOTER_CONFIG;
|
||||
setFooterConfigState(EMPTY_FOOTER_CONFIG);
|
||||
setFooterConfig(EMPTY_FOOTER_CONFIG);
|
||||
setProviderTypeHint(null);
|
||||
setOrgSetupPhase(ORG_SETUP_PHASE.DETAILS);
|
||||
}, [
|
||||
@@ -235,8 +194,7 @@ export function useProviderWizardController({
|
||||
setWizardVariant(WIZARD_VARIANT.PROVIDER);
|
||||
setCurrentStep(PROVIDER_WIZARD_STEP.CONNECT);
|
||||
setOrgCurrentStep(ORG_WIZARD_STEP.SETUP);
|
||||
footerConfigRef.current = EMPTY_FOOTER_CONFIG;
|
||||
setFooterConfigState(EMPTY_FOOTER_CONFIG);
|
||||
setFooterConfig(EMPTY_FOOTER_CONFIG);
|
||||
setProviderTypeHint(null);
|
||||
setOrgSetupPhase(ORG_SETUP_PHASE.DETAILS);
|
||||
onOpenChange(false);
|
||||
@@ -265,26 +223,6 @@ export function useProviderWizardController({
|
||||
setCurrentStep(PROVIDER_WIZARD_STEP.LAUNCH);
|
||||
};
|
||||
|
||||
const updateFooterConfig: Dispatch<SetStateAction<WizardFooterConfig>> = (
|
||||
nextFooterConfig,
|
||||
) => {
|
||||
const currentFooterConfig = footerConfigRef.current;
|
||||
const resolvedNextFooterConfig =
|
||||
typeof nextFooterConfig === "function"
|
||||
? nextFooterConfig(currentFooterConfig)
|
||||
: nextFooterConfig;
|
||||
footerConfigRef.current = resolvedNextFooterConfig;
|
||||
|
||||
if (isSameFooterConfig(currentFooterConfig, resolvedNextFooterConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setFooterConfigState({
|
||||
...resolvedNextFooterConfig,
|
||||
...footerActionCallbacksRef.current,
|
||||
});
|
||||
};
|
||||
|
||||
const openOrganizationsFlow = () => {
|
||||
// AWS Organizations diverges from the credentials path the tour guides toward; end
|
||||
// it so it doesn't dangle on a step that no longer fits. No-op off-onboarding.
|
||||
@@ -292,8 +230,7 @@ export function useProviderWizardController({
|
||||
resetOrgWizard();
|
||||
setWizardVariant(WIZARD_VARIANT.ORGANIZATIONS);
|
||||
setOrgCurrentStep(ORG_WIZARD_STEP.SETUP);
|
||||
footerConfigRef.current = EMPTY_FOOTER_CONFIG;
|
||||
setFooterConfigState(EMPTY_FOOTER_CONFIG);
|
||||
setFooterConfig(EMPTY_FOOTER_CONFIG);
|
||||
setProviderTypeHint(null);
|
||||
setOrgSetupPhase(ORG_SETUP_PHASE.DETAILS);
|
||||
};
|
||||
@@ -302,8 +239,7 @@ export function useProviderWizardController({
|
||||
resetOrgWizard();
|
||||
setWizardVariant(WIZARD_VARIANT.PROVIDER);
|
||||
setCurrentStep(PROVIDER_WIZARD_STEP.CONNECT);
|
||||
footerConfigRef.current = EMPTY_FOOTER_CONFIG;
|
||||
setFooterConfigState(EMPTY_FOOTER_CONFIG);
|
||||
setFooterConfig(EMPTY_FOOTER_CONFIG);
|
||||
setProviderTypeHint(null);
|
||||
setOrgSetupPhase(ORG_SETUP_PHASE.DETAILS);
|
||||
};
|
||||
@@ -312,13 +248,11 @@ export function useProviderWizardController({
|
||||
const docsLink = isProviderFlow
|
||||
? getProviderHelpText(providerTypeHint ?? providerType ?? "").link
|
||||
: DOCS_URLS.AWS_ORGANIZATIONS;
|
||||
const resolvedFooterConfig: WizardFooterConfig = footerConfig;
|
||||
const modalTitle = getProviderWizardModalTitle(mode);
|
||||
|
||||
return {
|
||||
currentStep,
|
||||
docsLink,
|
||||
footerConfig,
|
||||
handleClose,
|
||||
handleDialogOpenChange,
|
||||
handleTestSuccess,
|
||||
@@ -332,7 +266,7 @@ export function useProviderWizardController({
|
||||
providerTypeHint,
|
||||
resolvedFooterConfig,
|
||||
setCurrentStep,
|
||||
setFooterConfig: updateFooterConfig,
|
||||
setFooterConfig,
|
||||
setOrgCurrentStep,
|
||||
setOrgSetupPhase,
|
||||
setProviderTypeHint,
|
||||
|
||||
@@ -39,7 +39,7 @@ describe("ConnectStep", () => {
|
||||
useProviderWizardStore.getState().reset();
|
||||
});
|
||||
|
||||
it("does not publish a new footer config when form UI state is unchanged", async () => {
|
||||
it("does not republish footer config for repeated unchanged form UI state", async () => {
|
||||
// Given
|
||||
const onFooterChange = vi.fn();
|
||||
|
||||
@@ -56,12 +56,19 @@ describe("ConnectStep", () => {
|
||||
|
||||
// When
|
||||
act(() => {
|
||||
capturedConnectAccountFormProps.current?.onUiStateChange?.({
|
||||
const unchangedUiState = {
|
||||
showBack: false,
|
||||
showAction: false,
|
||||
actionLabel: "Next",
|
||||
actionDisabled: true,
|
||||
isLoading: false,
|
||||
};
|
||||
|
||||
capturedConnectAccountFormProps.current?.onUiStateChange?.(
|
||||
unchangedUiState,
|
||||
);
|
||||
capturedConnectAccountFormProps.current?.onUiStateChange?.({
|
||||
...unchangedUiState,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user