package.src.components.Wizard.Wizard.tsx Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of react-core Show documentation
Show all versions of react-core Show documentation
This library provides a set of common React components for use with the PatternFly reference implementation.
The newest version!
import React from 'react';
import { css } from '@patternfly/react-styles';
import styles from '@patternfly/react-styles/css/components/Wizard/wizard';
import {
isWizardParentStep,
WizardStepType,
isCustomWizardNav,
WizardFooterType,
WizardNavType,
WizardStepChangeScope
} from './types';
import { buildSteps, isStepEnabled } from './utils';
import { useWizardContext, WizardContextProvider } from './WizardContext';
import { WizardToggle } from './WizardToggle';
import { WizardNavInternal } from './WizardNavInternal';
/**
* Wrapper for all steps and hosts state, including navigation helpers, within context.
* The WizardContext provided by default gives any child of wizard access to those resources.
*/
export interface WizardProps extends React.HTMLProps {
/** Step components */
children: React.ReactNode;
/** Wizard header */
header?: React.ReactNode;
/** Wizard footer */
footer?: WizardFooterType;
/** Wizard navigation */
nav?: WizardNavType;
/** Aria-label for the Nav */
navAriaLabel?: string;
/** The initial index the wizard is to start on (1 or higher). Defaults to 1. */
startIndex?: number;
/** Additional classes spread to the wizard */
className?: string;
/** Custom width of the wizard */
width?: number | string;
/** Custom height of the wizard */
height?: number | string;
/** Disables steps that haven't been visited. Defaults to false. */
isVisitRequired?: boolean;
/** Progressively shows steps, where all steps following the active step are hidden. Defaults to false. */
isProgressive?: boolean;
/** Callback function when navigating between steps */
onStepChange?: (
event: React.MouseEvent,
currentStep: WizardStepType,
prevStep: WizardStepType,
scope: WizardStepChangeScope
) => void | Promise;
/** Callback function to save at the end of the wizard, if not specified uses onClose */
onSave?: (event: React.MouseEvent) => void | Promise;
/** Callback function to close the wizard */
onClose?: (event: React.MouseEvent) => void;
}
export const Wizard = ({
children,
footer,
height,
width,
className,
header,
nav,
navAriaLabel,
startIndex = 1,
isVisitRequired = false,
isProgressive = false,
onStepChange,
onSave,
onClose,
...wrapperProps
}: WizardProps) => {
const [activeStepIndex, setActiveStepIndex] = React.useState(startIndex);
const initialSteps = buildSteps(children);
const firstStepRef = React.useRef(initialSteps[startIndex - 1]);
// When the startIndex maps to a parent step, focus on the first sub-step
React.useEffect(() => {
if (isWizardParentStep(firstStepRef.current)) {
setActiveStepIndex(startIndex + 1);
}
}, [startIndex]);
const goToNextStep = (event: React.MouseEvent, steps: WizardStepType[] = initialSteps) => {
const newStep = steps.find((step) => step.index > activeStepIndex && isStepEnabled(steps, step));
if (activeStepIndex >= steps.length || !newStep?.index) {
return onSave ? onSave(event) : onClose?.(event);
}
setActiveStepIndex(newStep?.index);
onStepChange?.(event, newStep, steps[activeStepIndex - 1], WizardStepChangeScope.Next);
};
const goToPrevStep = (event: React.MouseEvent, steps: WizardStepType[] = initialSteps) => {
const newStep = [...steps]
.reverse()
.find((step: WizardStepType) => step.index < activeStepIndex && isStepEnabled(steps, step));
setActiveStepIndex(newStep?.index);
onStepChange?.(event, newStep, steps[activeStepIndex - 1], WizardStepChangeScope.Back);
};
const goToStepByIndex = (
event: React.MouseEvent,
steps: WizardStepType[] = initialSteps,
index: number
) => {
const lastStepIndex = steps.length + 1;
// Handle index when out of bounds or hidden
if (index < 1) {
index = 1;
} else if (index > lastStepIndex) {
index = lastStepIndex;
}
const currStep = steps[index - 1];
const prevStep = steps[activeStepIndex - 1];
setActiveStepIndex(index);
onStepChange?.(event, currStep, prevStep, WizardStepChangeScope.Nav);
};
const goToStepById = (steps: WizardStepType[] = initialSteps, id: number | string) => {
const step = steps.find((step) => step.id === id);
const stepIndex = step?.index;
const lastStepIndex = steps.length + 1;
if (stepIndex > 0 && stepIndex < lastStepIndex && !step.isDisabled && !step.isHidden) {
setActiveStepIndex(stepIndex);
}
};
const goToStepByName = (steps: WizardStepType[] = initialSteps, name: string) => {
const step = steps.find((step) => step.name === name);
const stepIndex = step?.index;
const lastStepIndex = steps.length + 1;
if (stepIndex > 0 && stepIndex < lastStepIndex && !step.isDisabled && !step.isHidden) {
setActiveStepIndex(stepIndex);
}
};
return (
{header}
);
};
const WizardInternal = ({
nav,
navAriaLabel,
isVisitRequired,
isProgressive
}: Pick) => {
const { activeStep, steps, footer, goToStepByIndex } = useWizardContext();
const [isNavExpanded, setIsNavExpanded] = React.useState(false);
const wizardNav = React.useMemo(() => {
if (isCustomWizardNav(nav)) {
return typeof nav === 'function' ? nav(isNavExpanded, steps, activeStep, goToStepByIndex) : nav;
}
return (
);
}, [activeStep, isVisitRequired, isProgressive, goToStepByIndex, isNavExpanded, nav, navAriaLabel, steps]);
return (
setIsNavExpanded((prevIsExpanded) => !prevIsExpanded)}
/>
);
};
Wizard.displayName = 'Wizard';