Skip to content

Commit 93ac9da

Browse files
committed
#59 - implementing validation classes management, fix all tests
1 parent cf7f1f6 commit 93ac9da

17 files changed

Lines changed: 175 additions & 62 deletions

src/BitBlazor/Form/BitFormComponentBase.cs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ namespace BitBlazor.Form;
99
/// Represents the base class for the form components
1010
/// </summary>
1111
/// <typeparam name="T">The data type supported by the component</typeparam>
12-
public abstract class BitFormComponentBase<T> : BitComponentBase
12+
public abstract class BitFormComponentBase<T> : BitComponentBase, IDisposable
1313
{
14+
private string validationCssClass = string.Empty;
15+
private bool disposedValue;
16+
1417
/// <summary>
1518
/// Gets or sets the <see cref="EditContext"/> instance in case of use of an <see cref="EditForm"/>
1619
/// </summary>
@@ -112,6 +115,30 @@ protected BitFormComponentBase()
112115
/// </remarks>
113116
protected Type ComponentType => Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
114117

118+
protected override void OnInitialized()
119+
{
120+
base.OnInitialized();
121+
if (CurrentEditContext is not null)
122+
{
123+
CurrentEditContext.OnValidationStateChanged += OnFieldValidationStateChanged;
124+
}
125+
}
126+
127+
private void OnFieldValidationStateChanged(object? sender, ValidationStateChangedEventArgs e)
128+
{
129+
if (ValueExpression is not null)
130+
{
131+
var fieldIdentifier = FieldIdentifier.Create(ValueExpression);
132+
var fieldValidationCssClass = CurrentEditContext!.IsValid(fieldIdentifier) ? "just-validate-success-field" : "is-invalid";
133+
134+
if (fieldValidationCssClass != validationCssClass)
135+
{
136+
validationCssClass = fieldValidationCssClass;
137+
InvokeAsync(StateHasChanged);
138+
}
139+
}
140+
}
141+
115142
/// <inheritdoc/>
116143
protected override void OnParametersSet()
117144
{
@@ -157,6 +184,44 @@ private void SetAdditionalTextAttributes()
157184
}
158185
}
159186

187+
/// <summary>
188+
/// Updates the validation CSS class based on the current validation state of the field.
189+
/// </summary>
190+
/// <remarks>
191+
/// This method checks the EditContext for validation state and applies the appropriate CSS class:
192+
/// <list type="bullet">
193+
/// <item><description>"is-invalid" - field has validation errors (shown immediately when validation runs)</description></item>
194+
/// <item><description>"just-validate-success-field" - field is modified and is valid</description></item>
195+
/// <item><description>Empty string - field is valid and has not been modified</description></item>
196+
/// </list>
197+
/// </remarks>
198+
private void UpdateValidationCssClass()
199+
{
200+
if (CurrentEditContext is null || ValueExpression is null)
201+
{
202+
validationCssClass = string.Empty;
203+
return;
204+
}
205+
206+
var fieldIdentifier = FieldIdentifier.Create(ValueExpression);
207+
208+
validationCssClass = CurrentEditContext.IsValid(fieldIdentifier) ? "just-validate-success-field" : "is-invalid";
209+
}
210+
211+
/// <summary>
212+
/// Adds the Bootstrap Italia validation CSS class to the provided <see cref="CssClassBuilder"/>.
213+
/// </summary>
214+
/// <param name="builder">The CSS class builder to add the validation class to.</param>
215+
/// <remarks>
216+
/// Adds "is-invalid" for invalid fields, "just-validate-success-field" for valid modified fields,
217+
/// or nothing for unmodified fields. This method should be called when building the CSS classes
218+
/// for form input elements.
219+
/// </remarks>
220+
protected void AddValidationCssClass(CssClassBuilder builder)
221+
{
222+
builder.Add(validationCssClass);
223+
}
224+
160225
/// <summary>
161226
/// Renders a validation message for the specified field.
162227
/// </summary>
@@ -209,4 +274,27 @@ private void SetAdditionalTextAttributes()
209274
builder.CloseElement();
210275
};
211276
}
277+
278+
protected virtual void Dispose(bool disposing)
279+
{
280+
if (!disposedValue)
281+
{
282+
if (disposing)
283+
{
284+
if (CurrentEditContext is not null)
285+
{
286+
CurrentEditContext.OnValidationStateChanged -= OnFieldValidationStateChanged;
287+
}
288+
}
289+
290+
disposedValue = true;
291+
}
292+
}
293+
294+
void IDisposable.Dispose()
295+
{
296+
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
297+
Dispose(disposing: true);
298+
GC.SuppressFinalize(this);
299+
}
212300
}

src/BitBlazor/Form/BitInputFieldBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ protected virtual string ComputeInputCssClass()
8282

8383
AddDefaultCssClass(builder);
8484
AddSizeCssClass(builder);
85+
AddValidationCssClass(builder);
8586
AddCustomCssClass(builder);
8687

8788
return builder.Build();

src/BitBlazor/Form/Checkbox/BitCheckbox.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ValueExpression="ValueExpression"
99
name="@Id"
1010
disabled="@Disabled"
11+
class="@ComputeInputCssClass()"
1112
@attributes="AdditionalAttributes" />
1213

1314
<label for="@Id" class="@ComputeLabelCssClass()">@Label</label>

src/BitBlazor/Form/Checkbox/BitCheckbox.razor.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ private string ComputeContainerCssClasses()
5050
return builder.Build();
5151
}
5252

53+
private string ComputeInputCssClass()
54+
{
55+
var builder = new CssClassBuilder();
56+
AddValidationCssClass(builder);
57+
return builder.Build();
58+
}
59+
5360
private string ComputeLabelCssClass()
5461
{
5562
var builder = new CssClassBuilder();

src/BitBlazor/Form/SelectField/BitSelectField.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
ValueExpression="ValueExpression"
1212
name="@Id"
1313
disabled="@Disabled"
14+
class="@ComputeInputCssClass()"
1415
@attributes="AdditionalAttributes">
1516
<CascadingValue Value="this">
1617
@ChildContent

src/BitBlazor/Form/SelectField/BitSelectField.razor.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,11 @@ private string ComputeContainerCssClass()
3333

3434
return builder.Build();
3535
}
36+
37+
private string ComputeInputCssClass()
38+
{
39+
var builder = new CssClassBuilder();
40+
AddValidationCssClass(builder);
41+
return builder.Build();
42+
}
3643
}

src/BitBlazor/Form/Toggle/BitToggle.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
ValueExpression="ValueExpression"
1212
name="@Id"
1313
disabled="@Disabled"
14+
class="@ComputeInputCssClass()"
1415
@attributes="AdditionalAttributes" />
1516
<span class="lever"></span>
1617
</label>

src/BitBlazor/Form/Toggle/BitToggle.razor.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public partial class BitToggle : BitFormComponentBase<bool>
3232
private string ComputeContainerCssClass()
3333
{
3434
var builder = new CssClassBuilder("form-check");
35-
35+
3636
var viewModeClass = ViewMode switch
3737
{
3838
ToggleViewMode.Grouped => "form-check-group",
@@ -45,4 +45,11 @@ private string ComputeContainerCssClass()
4545

4646
return builder.Build();
4747
}
48+
49+
private string ComputeInputCssClass()
50+
{
51+
var builder = new CssClassBuilder();
52+
AddValidationCssClass(builder);
53+
return builder.Build();
54+
}
4855
}

tests/BitBlazor.Test/Form/Checkbox/BitCheckboxTest.Rendering.razor

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@
129129
</EditForm>);
130130

131131
// Trigger validation by submitting the form
132-
var submitButton = component.Find("#submit-btn");
133-
submitButton.Click();
132+
var form = component.Find("form");
133+
form.Submit();
134134

135135
var input = component.Find("input#test-accept");
136136
Assert.Contains("is-invalid", input.ClassList);
@@ -156,8 +156,8 @@
156156
</EditForm>);
157157

158158
// Trigger validation by submitting the form
159-
var submitButton = component.Find("#submit-btn");
160-
submitButton.Click();
159+
var form = component.Find("form");
160+
form.Submit();
161161

162162
var input = component.Find("input#test-accept");
163163
Assert.Contains("just-validate-success-field", input.ClassList);
@@ -182,8 +182,8 @@
182182
</EditForm>);
183183

184184
// Trigger validation by submitting the form
185-
var submitButton = component.Find("#submit-btn");
186-
submitButton.Click();
185+
var form = component.Find("form");
186+
form.Submit();
187187

188188
var validationMessage = component.Find(".just-validate-error-label");
189189
Assert.NotNull(validationMessage);

tests/BitBlazor.Test/Form/Datepicker/BitDatepickerTest.Rendering.razor

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@
5959
</EditForm>);
6060

6161
// Trigger validation by submitting the form
62-
var submitButton = component.Find("#submit-btn");
63-
submitButton.Click();
62+
var form = component.Find("form");
63+
form.Submit();
6464

6565
var input = component.Find("input#test-birthdate");
6666
Assert.Contains("is-invalid", input.ClassList);
@@ -87,8 +87,8 @@
8787
</EditForm>);
8888

8989
// Trigger validation by submitting the form
90-
var submitButton = component.Find("#submit-btn");
91-
submitButton.Click();
90+
var form = component.Find("form");
91+
form.Submit();
9292

9393
var input = component.Find("input#test-birthdate");
9494
Assert.Contains("just-validate-success-field", input.ClassList);
@@ -114,8 +114,8 @@
114114
</EditForm>);
115115

116116
// Trigger validation by submitting the form
117-
var submitButton = component.Find("#submit-btn");
118-
submitButton.Click();
117+
var form = component.Find("form");
118+
form.Submit();
119119

120120
var validationMessage = component.Find(".just-validate-error-label");
121121
Assert.NotNull(validationMessage);

0 commit comments

Comments
 (0)