Skip to content

Commit 9a2deb5

Browse files
authored
Stepper: Fix Prevent Func Called Twice (#169)
* Stepper Fix Prevent Func Called Twice * Add Test
1 parent 30abe70 commit 9a2deb5

File tree

3 files changed

+226
-9
lines changed

3 files changed

+226
-9
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
@namespace MudExtensions.UnitTests.TestComponents
2+
@inject ISnackbar Snackbar
3+
@inject NavigationManager NavigationManager
4+
@using MudBlazor.Extensions
5+
@using MudExtensions.Enums;
6+
@using MudExtensions.Utilities
7+
8+
<MudGrid Class="cursor-default">
9+
<MudItem xs="12" sm="8">
10+
<MudStepper @ref="_stepper" ContentStyle="min-height: 400px" Linear="_linear" Vertical="_vertical" Color="_color" Variant="_variant"
11+
DisableAnimation="_disableAnimation" DisablePreviousButton="_disablePreviousButton" DisableNextButton="_disableNextButton"
12+
DisableSkipButton="_disableSkipButton" DisableStepResultIndicator="_disableStepResultIndicator" HeaderTextView="_headerTextView"
13+
PreventStepChangeAsync="new Func<StepChangeDirection, Task<bool>>(CheckChange)" LocalizedStrings="GetLocalizedStrings()"
14+
MobileView="_mobileView" IconActionButtons="_iconActionButtons" Loading="_loading">
15+
<StaticContent>
16+
@if (_showStaticContent)
17+
{
18+
<MudStack Row="true" AlignItems="AlignItems.Center">
19+
<MudAvatar Color="_color">ST</MudAvatar>
20+
<MudText>This is a static content which shows with each step.</MudText>
21+
</MudStack>
22+
}
23+
</StaticContent>
24+
<ChildContent>
25+
<MudStep Title="Customer Info" StatusChanged="StatusChanged">
26+
<MudForm @ref="_form">
27+
<MudStack>
28+
<MudTextField T="string" Label="Name" Variant="_variant" Required="true" />
29+
<MudTextField T="string" Label="Last Name" Variant="_variant" />
30+
<MudTextField T="string" Label="Adress" Variant="_variant" />
31+
</MudStack>
32+
</MudForm>
33+
</MudStep>
34+
<MudStep Title="Booking Spec." Optional="true">
35+
<MudCheckBox T="bool" Label="Early Check-in" Color="_color" />
36+
<MudCheckBox T="bool" Label="Late Check-out" Color="_color" />
37+
<MudCheckBox T="bool" Label="Twin Bed" Color="_color" />
38+
</MudStep>
39+
<MudStep Title="Payment">
40+
<MudForm @ref="_form2">
41+
<MudStack>
42+
<MudTextField T="string" Label="Card Number" Variant="_variant" Required="true" />
43+
<MudStack Row="true">
44+
<MudTextField T="string" Label="Expire Date" Variant="_variant" Required="true" />
45+
<MudTextField T="string" Label="CVC" Variant="_variant" Required="true" />
46+
</MudStack>
47+
</MudStack>
48+
</MudForm>
49+
</MudStep>
50+
51+
@if (_addResultStep)
52+
{
53+
<MudStep Title="Result Step" IsResultStep="true">
54+
<div class="d-flex flex-column align-center justify-center" style="height: 200px">
55+
<MudIconButton Icon="@Icons.Material.Filled.DoneAll" Size="Size.Large" Variant="Variant.Filled" Color="Color.Success" />
56+
<MudText>Your reservation succesfully completed.</MudText>
57+
</div>
58+
</MudStep>
59+
}
60+
</ChildContent>
61+
<ActionContent>
62+
@if (!_stepper.IsAllStepsCompleted())
63+
{
64+
<MudButton Color="Color.Secondary" Variant="_variant" OnClick="@(() => Snackbar.Add("Custom cancel button clicked.", Severity.Info))">Cancel</MudButton>
65+
}
66+
<MudSpacer />
67+
</ActionContent>
68+
</MudStepper>
69+
</MudItem>
70+
71+
<MudItem xs="12" sm="4" Style="box-shadow: rgba(240, 46, 170, 0.4) -3px 3px;">
72+
<MudStack Spacing="4">
73+
<MudNumericField @bind-Value="_activeIndex" Label="Change ActiveIndex" @bind-Value:after="(() => _stepper.SetActiveStepByIndex(_activeIndex))" Margin="Margin.Dense" />
74+
<MudCheckBox @bind-Checked="_linear" Color="Color.Primary" Label="Linear" Dense="true" />
75+
<MudCheckBox @bind-Checked="_disableAnimation" Color="Color.Primary" Label="Disable Animation" Dense="true" />
76+
<MudCheckBox @bind-Checked="_disablePreviousButton" Color="Color.Primary" Label="Disable Previous Step Action Button" Dense="true" />
77+
<MudCheckBox @bind-Checked="_disableNextButton" Color="Color.Primary" Label="Disable Next Step Action Button" Dense="true" />
78+
<MudCheckBox @bind-Checked="_disableSkipButton" Color="Color.Primary" Label="Disable Skip Step Action Button" Dense="true" />
79+
<MudCheckBox @bind-Checked="_disableStepResultIndicator" Color="Color.Primary" Label="Disable Step Result Indicator" Dense="true" />
80+
<MudSwitchM3 @bind-Checked="_mobileView" Color="Color.Primary" Label="Mobile View" />
81+
<MudSwitchM3 @bind-Checked="_iconActionButtons" Color="Color.Primary" Label="IconActionButtons" />
82+
<MudSwitchM3 @bind-Checked="_addResultStep" Color="Color.Primary" Label="Has Result Step" />
83+
<MudSwitchM3 @bind-Checked="_checkValidationBeforeComplete" Color="Color.Primary" Label="Check Validation Before Complete Step" />
84+
<MudSwitchM3 @bind-Checked="_customLocalization" Color="Color.Primary" Label="Custom Localization (German)" />
85+
<MudSwitchM3 @bind-Checked="_showStaticContent" Color="Color.Primary" Label="Show Some Static Content" />
86+
<MudSelect @bind-Value="_variant" Variant="Variant.Outlined" Label="Variant">
87+
@foreach (Variant item in Enum.GetValues<Variant>())
88+
{
89+
<MudSelectItem Value="item">@item.ToDescriptionString()</MudSelectItem>
90+
}
91+
</MudSelect>
92+
<MudSelect @bind-Value="_color" Variant="Variant.Outlined" Label="Color">
93+
@foreach (Color item in Enum.GetValues<Color>())
94+
{
95+
<MudSelectItem Value="item">@item.ToDescriptionString()</MudSelectItem>
96+
}
97+
</MudSelect>
98+
<MudSelect @bind-Value="_headerTextView" Variant="Variant.Outlined" Label="Header Text View">
99+
@foreach (HeaderTextView item in Enum.GetValues<HeaderTextView>())
100+
{
101+
<MudSelectItem Value="item">@item.ToDescriptionString()</MudSelectItem>
102+
}
103+
</MudSelect>
104+
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="(() => _stepper.Reset())">Reset</MudButton>
105+
</MudStack>
106+
</MudItem>
107+
</MudGrid>
108+
109+
@code {
110+
MudStepper _stepper;
111+
MudForm _form;
112+
MudForm _form2;
113+
bool _checkValidationBeforeComplete = false;
114+
bool _linear;
115+
bool _mobileView;
116+
bool _iconActionButtons;
117+
Variant _variant = Variant.Filled;
118+
HeaderTextView _headerTextView = HeaderTextView.All;
119+
bool _disableAnimation = false;
120+
bool _disablePreviousButton = false;
121+
bool _disableNextButton = false;
122+
bool _disableSkipButton = false;
123+
bool _disableStepResultIndicator = false;
124+
bool _addResultStep = true;
125+
bool _customLocalization = false;
126+
Color _color = Color.Primary;
127+
int _activeIndex = 0;
128+
bool _loading;
129+
bool _showStaticContent = false;
130+
131+
[Parameter]
132+
public int CheckChangeCount { get; set; }
133+
134+
private async Task<bool> CheckChange(StepChangeDirection direction)
135+
{
136+
CheckChangeCount += 1;
137+
if (_checkValidationBeforeComplete == true)
138+
{
139+
// Always allow stepping backwards, even if forms are invalid
140+
if (direction == StepChangeDirection.Backward)
141+
{
142+
return false;
143+
}
144+
if (_stepper.GetActiveIndex() == 0)
145+
{
146+
_loading = true;
147+
StateHasChanged();
148+
await Task.Delay(1000);
149+
await _form.Validate();
150+
_loading = false;
151+
StateHasChanged();
152+
return !_form.IsValid;
153+
}
154+
else if (_stepper.GetActiveIndex() == 2)
155+
{
156+
_loading = true;
157+
StateHasChanged();
158+
await Task.Delay(1000);
159+
await _form2.Validate();
160+
_loading = false;
161+
StateHasChanged();
162+
return !_form2.IsValid;
163+
}
164+
else
165+
{
166+
return false;
167+
}
168+
}
169+
else
170+
{
171+
return false;
172+
}
173+
}
174+
175+
private void StatusChanged(StepStatus status)
176+
{
177+
Snackbar.Add($"First step {status.ToDescriptionString()}.", Severity.Info);
178+
}
179+
180+
private StepperLocalizedStrings GetLocalizedStrings()
181+
{
182+
if (_customLocalization == false)
183+
{
184+
return new StepperLocalizedStrings();
185+
}
186+
else
187+
{
188+
return new StepperLocalizedStrings()
189+
{
190+
Completed = "Abgeschlossen",
191+
Finish = "Fertig",
192+
Next = "Nächste",
193+
Optional = "Optional",
194+
Previous = "Früher",
195+
Skip = "überspringen",
196+
Skipped = "übersprungen",
197+
};
198+
}
199+
}
200+
}

CodeBeam.MudBlazor.Extensions.UnitTests/Components/StepperTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using MudBlazor;
44
using MudExtensions;
55
using MudExtensions.Enums;
6+
using MudExtensions.UnitTests.TestComponents;
67

78
namespace MudExtensions.UnitTests.Components
89
{
@@ -314,5 +315,20 @@ public async Task StepperActiveIndexIsNotChangedWhenSkippingNonActiveStepTest()
314315
// Assert
315316
stepper.Instance.ActiveIndex.Should().Be(stepper.Instance.Steps.IndexOf(step0.Instance));
316317
}
318+
319+
[Test]
320+
public async Task StepperCheckChangeCountTest()
321+
{
322+
// Arrange
323+
var comp = Context.RenderComponent<StepperTest1>();
324+
var stepper = comp.FindComponent<MudStepper>();
325+
comp.Instance.CheckChangeCount.Should().Be(0);
326+
327+
await comp.InvokeAsync(() => stepper.Instance.SetActiveIndex(1));
328+
comp.WaitForAssertion(() => comp.Instance.CheckChangeCount.Should().Be(1));
329+
330+
await comp.InvokeAsync(() => stepper.Instance.SetActiveStepByIndex(0));
331+
comp.WaitForAssertion(() => comp.Instance.CheckChangeCount.Should().Be(2));
332+
}
317333
}
318334
}

CodeBeam.MudBlazor.Extensions/Components/Stepper/MudStepper.razor.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,6 @@
44
using MudBlazor.Extensions;
55
using MudBlazor.Utilities;
66
using MudExtensions.Enums;
7-
using System;
8-
using System.Collections.Generic;
9-
using System.Linq;
10-
using System.Security.Cryptography.X509Certificates;
11-
using System.Text;
12-
using System.Threading.Tasks;
13-
using static MudBlazor.Colors;
14-
using MudExtensions.Extensions;
157

168
namespace MudExtensions
179
{
@@ -235,7 +227,7 @@ public async Task SetActiveIndex(int count, bool firstCompleted = false, bool sk
235227
return;
236228
}
237229

238-
if (PreventStepChangeAsync != null)
230+
if (skipPreventProcess == false && PreventStepChangeAsync != null)
239231
{
240232
var result = await PreventStepChangeAsync.Invoke(stepChangeDirection);
241233
if (result == true)
@@ -294,6 +286,15 @@ public async Task SetActiveStepByIndex(int index, bool firstCompleted = false, b
294286
return;
295287
}
296288

289+
if (skipPreventProcess == false && PreventStepChangeAsync != null)
290+
{
291+
var result = await PreventStepChangeAsync.Invoke(stepChangeDirection);
292+
if (result == true)
293+
{
294+
return;
295+
}
296+
}
297+
297298
if (ActiveIndex == index || index < 0 || Steps.Count < index)
298299
{
299300
return;

0 commit comments

Comments
 (0)