Skip to content

Commit 0a6ef94

Browse files
feat: allow jumping forward and back between completed steps of stepper (#1089)
Co-authored-by: anserwaseem <hafiz.anser.waseem@gmail.com>
1 parent 252b869 commit 0a6ef94

2 files changed

Lines changed: 29 additions & 8 deletions

File tree

apps/kitchen-sink/src/ensemble/screens/widgets.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,8 +1178,12 @@ View:
11781178
iconName: ThumbUpAltOutlined
11791179
name: step
11801180
template:
1181-
Icon:
1182-
name: Search
1181+
Row:
1182+
children:
1183+
- Icon:
1184+
name: ${step.iconName}
1185+
- Text:
1186+
text: ${step.name}
11831187
- Button:
11841188
label: Next
11851189
onTap:

packages/runtime/src/widgets/Stepper/Stepper.tsx

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import Alert from "@mui/material/Alert";
2727
import type { EnsembleWidgetProps, HasItemTemplate } from "../../shared/types";
2828
import { EnsembleRuntime } from "../../runtime";
2929
import { WidgetRegistry } from "../../registry";
30+
import { useEnsembleAction } from "../../runtime/hooks";
3031
import { StepType } from "./StepType";
3132
import type { StepTypeProps } from "./StepType";
32-
import { useEnsembleAction } from "../../runtime/hooks";
3333

3434
const widgetName = "Stepper";
3535

@@ -61,6 +61,7 @@ interface CustomConnectorProps {
6161

6262
const Stepper: React.FC<StepperProps> = (props) => {
6363
const [activeStep, setActiveStep] = useState<number | undefined>(undefined);
64+
const [maxReachedStep, setMaxReachedStep] = useState<number>(0);
6465

6566
const itemTemplate = props["item-template"];
6667
const { namedData } = useTemplateData({ ...itemTemplate });
@@ -70,9 +71,15 @@ const Stepper: React.FC<StepperProps> = (props) => {
7071

7172
const handleNext = useCallback(() => {
7273
if (activeStep !== undefined && activeStep < namedData.length - 1) {
73-
setActiveStep(activeStep + 1);
74+
const nextStep = activeStep + 1;
75+
setActiveStep(nextStep);
76+
77+
// update max reached if the next step is greater than the current max
78+
if (nextStep > maxReachedStep) {
79+
setMaxReachedStep(nextStep);
80+
}
7481
}
75-
}, [activeStep, namedData.length]);
82+
}, [activeStep, namedData.length, maxReachedStep]);
7683

7784
const handleBack = useCallback(() => {
7885
if (activeStep) {
@@ -94,7 +101,14 @@ const Stepper: React.FC<StepperProps> = (props) => {
94101
isExpression(props.activeStepIndex) &&
95102
isNumber(values?.activeStepIndex)
96103
) {
97-
setActiveStep(values?.activeStepIndex ?? 0);
104+
const newActiveStep = values?.activeStepIndex ?? 0;
105+
setActiveStep(newActiveStep);
106+
107+
// for the initial setup, if we're on step N, it means steps 0 to N-1 are completed
108+
// and we should be able to navigate between all of them + the current step
109+
setMaxReachedStep((prev) => {
110+
return Math.max(prev, newActiveStep);
111+
});
98112
}
99113
}, [props.activeStepIndex, values?.activeStepIndex]);
100114

@@ -157,8 +171,11 @@ const Stepper: React.FC<StepperProps> = (props) => {
157171
}}
158172
>
159173
{namedData.map((data, index) => (
160-
<Step key={index}>
161-
<StepButton onClick={onChangeCallback(index)}>
174+
<Step completed={index <= maxReachedStep} key={index}>
175+
<StepButton
176+
disabled={index > maxReachedStep}
177+
onClick={onChangeCallback(index)}
178+
>
162179
<StepLabel
163180
StepIconComponent={(iconProps: StepOwnProps) => {
164181
const newProps = {

0 commit comments

Comments
 (0)