From 24d438a4d0fea626f1c136bd65ab96e0985a72a1 Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Mon, 10 Feb 2025 17:48:29 +0100 Subject: [PATCH 1/8] Create to FleuntUiCheckbox Update CheckboxThreeStates and FluentCheckbox components Updated CheckboxThreeStates.razor to include additional FluentCheckbox components and modify existing ones. Renamed variables and added new ones in the @code section. Modified OnInitializedAsync and SetCheckStateChangedAsync methods in FluentCheckbox.razor.cs to handle value changes more effectively. WIP Add unit testing --- .../Examples/CheckboxAppearances.razor | 4 + .../Checkbox/Examples/CheckboxDisabled.razor | 7 + .../Examples/CheckboxIndeterminate.razor | 18 ++ .../Checkbox/Examples/CheckboxSizes.razor | 21 ++ .../Examples/CheckboxThreeStates.razor | 52 ++++ .../Components/Checkbox/FluentCheckbox.md | 53 ++++ .../FluentUI.Demo.Client.csproj | 2 +- .../Components/Checkbox/FluentCheckbox.razor | 46 ++++ .../Checkbox/FluentCheckbox.razor.cs | 240 ++++++++++++++++++ src/Core/Enums/CheckboxShape.cs | 25 ++ src/Core/Enums/CheckboxSize.cs | 25 ++ tests/Core/Components.Tests.csproj | 2 +- ...FluentCheckbox_Default.verified.razor.html | 2 + ...Checkbox_LabelTemplate.verified.razor.html | 6 + ...ckbox_StartEndTemplate.verified.razor.html | 4 + .../Checkbox/FluentCheckboxTests.razor | 184 ++++++++++++++ 16 files changed, 689 insertions(+), 2 deletions(-) create mode 100644 examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxAppearances.razor create mode 100644 examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDisabled.razor create mode 100644 examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxIndeterminate.razor create mode 100644 examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxSizes.razor create mode 100644 examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxThreeStates.razor create mode 100644 examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md create mode 100644 src/Core/Components/Checkbox/FluentCheckbox.razor create mode 100644 src/Core/Components/Checkbox/FluentCheckbox.razor.cs create mode 100644 src/Core/Enums/CheckboxShape.cs create mode 100644 src/Core/Enums/CheckboxSize.cs create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Default.verified.razor.html create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_LabelTemplate.verified.razor.html create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.razor diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxAppearances.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxAppearances.razor new file mode 100644 index 000000000..3ccd52fc9 --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxAppearances.razor @@ -0,0 +1,4 @@ +
+ + +
diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDisabled.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDisabled.razor new file mode 100644 index 000000000..99b18f0db --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDisabled.razor @@ -0,0 +1,7 @@ +
+ + + + + +
diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxIndeterminate.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxIndeterminate.razor new file mode 100644 index 000000000..f6959c350 --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxIndeterminate.razor @@ -0,0 +1,18 @@ +
+ +
+

Value is @(value ? "checked" : "unchecked")

+

CheckState is @(checkState is null ? "(null indeterminate)" : checkState.Value ? "checked" : "unchecked")

+ + Display current values in console +
+ +@code { + private bool value; + private bool? checkState; + + void OnClick(){ + var checkStateString = checkState is null ? "(null indeterminate)" : checkState.Value.ToString(); + Console.WriteLine($"Value: {value} - Checkstate : {checkStateString}"); + } +} diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxSizes.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxSizes.razor new file mode 100644 index 000000000..dcbf6a4d4 --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxSizes.razor @@ -0,0 +1,21 @@ +
+ + + + + + + + + +
+ + + + + + + + + +
diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxThreeStates.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxThreeStates.razor new file mode 100644 index 000000000..86b54fdef --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxThreeStates.razor @@ -0,0 +1,52 @@ +
+ + + Value = @value1 - CheckState is @(checkState1 is null ? "(null indeterminate)" : checkState1.Value.ToString()) +
+ + + + Value = @value2 +
+ + + Value: @valueThreeStateOrderUncheckToIntermediate - CheckState is + @(checkStateThreeStateOrderUncheckToIntermediate is null + ? "(null indeterminate)" + : checkStateThreeStateOrderUncheckToIntermediate.Value.ToString()) + +
+
+ + + Value: @valueShowIntermediateWithoutThreeState - CheckState is + @(checkStateShowIntermediateWithoutThreeState is null + ? "(null indeterminate)" + : checkStateShowIntermediateWithoutThreeState.Value.ToString()) +
+ +@code { + private bool? checkState1 = true; + private bool value1 = false; + private bool value2 = false; + private bool? checkStateThreeStateOrderUncheckToIntermediate = false; + private bool valueThreeStateOrderUncheckToIntermediate = false; + private bool valueShowIntermediateWithoutThreeState = false; + private bool? checkStateShowIntermediateWithoutThreeState = null; +} diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md new file mode 100644 index 000000000..1e3bf0cde --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md @@ -0,0 +1,53 @@ +--- +title: Checkbox +route: /Checkbox +--- + +# Checkbox + +A **FluentCheckbox** component enables a user to select or deselect an option. +It's typically used to capture a boolean value. + +## Appearance + +The apparent style of a checkbox can be changed by setting the `Shape` property, but also by setting the `Size` property. + +You can also add a label to the checkbox by setting the `Label` property. +The label will be automatically positioned next to the checkbox. + +We recommend using a spacing of 24px between checkboxes and other components. + +{{ CheckboxAppearances }} + +### Size + +The size of the checkbox can be adjusted using the `Size` property. The available sizes are: + +- `Medium`: The default size. +- `Large`: A larger size for the checkbox. + +{{ CheckboxSizes }} + +### Indeterminate + +The `FluentCheckbox` component supports an indeterminate state, which can be useful for scenarios where a checkbox represents a mixed or partial selection. +The indeterminate state is visually distinct from the checked and unchecked states. + +To set the checkbox to the indeterminate state, use the `Indeterminate` property. + +{{ CheckboxIndeterminate }} + +## Three-State Checkbox + +The `FluentCheckbox` component supports a three-state mode, which allows the checkbox to have an additional indeterminate state. This can be useful for scenarios where a checkbox represents a mixed or partial selection. + +To enable the three-state mode, set the `ThreeState` property to `true`. You can also control the order of the states using the `ThreeStateOrderUncheckToIntermediate` property. + +- `ThreeState`: Enables the three-state mode. +- `ThreeStateOrderUncheckToIntermediate`: Controls the order of the states. If set to `true`, the order will be Unchecked -> Intermediate -> Checked. If set to `false` (default), the order will be Unchecked -> Checked -> Intermediate. + +{{ CheckboxThreeStates }} + +## API FluentCheckbox + +{{ API Type=FluentCheckbox }} diff --git a/examples/Demo/FluentUI.Demo.Client/FluentUI.Demo.Client.csproj b/examples/Demo/FluentUI.Demo.Client/FluentUI.Demo.Client.csproj index aaacc6153..e5ffad94f 100644 --- a/examples/Demo/FluentUI.Demo.Client/FluentUI.Demo.Client.csproj +++ b/examples/Demo/FluentUI.Demo.Client/FluentUI.Demo.Client.csproj @@ -1,4 +1,4 @@ - + net9.0 diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor b/src/Core/Components/Checkbox/FluentCheckbox.razor new file mode 100644 index 000000000..34c163e6c --- /dev/null +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor @@ -0,0 +1,46 @@ +@namespace Microsoft.FluentUI.AspNetCore.Components +@using Microsoft.FluentUI.AspNetCore.Components.Extensions +@inherits FluentInputBase + + + @if (HasLabel()) + { + + } + + + + @if (StartTemplate != null) + { +
+ @StartTemplate +
+ } + + @if (EndTemplate != null) + { +
+ @EndTemplate +
+ } +
+
diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs new file mode 100644 index 000000000..75f2e090b --- /dev/null +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs @@ -0,0 +1,240 @@ +// ------------------------------------------------------------------------ +// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------ + +using System.Diagnostics.CodeAnalysis; +using System.Reflection.Metadata; +using Microsoft.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components.Utilities; +using Microsoft.JSInterop; + +namespace Microsoft.FluentUI.AspNetCore.Components; + +/// +/// The FluentCheckbox component is used to render a checkbox input +/// +public partial class FluentCheckbox : FluentInputBase, IFluentComponentElementBase +{ + + /// + [Parameter] + public ElementReference Element { get; set; } + + /// + /// Gets or sets a value indicating whether the user can display the indeterminate state by clicking the CheckBox. + /// + /// If this is not the case, the checkbox can be started in the indeterminate state, but the user cannot activate it with the mouse. + /// true + [Parameter] + public bool ShowIndeterminate { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the CheckBox will allow three check states rather than two. + /// + [Parameter] + public bool ThreeState { get; set; } + + /// + /// Gets or sets a value indicating the order of the three states of the CheckBox. + /// False(by default), the order is Unchecked -> Checked -> Intermediate. + /// True: the order is Unchecked -> Intermediate -> Checked. + /// + [Parameter] + public bool ThreeStateOrderUncheckToIntermediate { get; set; } + + /// + /// Gets or sets the shape of the checkbox + /// + [Parameter] + public CheckboxShape Shape { get; set; } = CheckboxShape.Square; + + /// + /// Gets or sets the content to prefix the input component. + /// + [Parameter] + public virtual RenderFragment? StartTemplate { get; set; } + + /// + /// Gets or sets the content to suffix the input component. + /// + [Parameter] + public virtual RenderFragment? EndTemplate { get; set; } + + /// + /// Gets or sets the state of the CheckBox: true, false or null. + /// Useful when the mode ThreeState is enable + /// + [Parameter] + public bool? CheckState { get; set; } + + /// + /// Action to be called when the CheckBox state changes. + /// + [Parameter] + public EventCallback CheckStateChanged { get; set; } + + /// + /// Gets or sets the size of the checkbox. See + /// + [Parameter] + public CheckboxSize Size { get; set; } = CheckboxSize.Medium; + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + + if (HasLabel()) + { + // When the id is not provided, generate a unique id. This allow to use the label for. + Id ??= $"{Identifier.NewId()}"; + } + + if (ThreeState && CheckState.HasValue) + { + await SetValueChangedAsync(CheckState.Value); + } + } + + /// + /// Parses a string to create the . + /// + /// The string value to be parsed. + /// The result to inject into the Value. + /// If the value could not be parsed, provides a validation error message. + /// True if the value could be parsed; otherwise false. + protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out bool result, [NotNullWhen(false)] out string? validationErrorMessage) + { + if (bool.TryParse(value, out var parsedValue)) + { + result = parsedValue; + validationErrorMessage = null; + return true; + } + + result = default; + validationErrorMessage = "The provided value is not a valid boolean."; + return false; + } + + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("Microsoft.FluentUI.Blazor.Utilities.Attributes.observeAttributeChange", Element, "checked", "boolean"); + await JSRuntime.InvokeVoidAsync("Microsoft.FluentUI.Blazor.Utilities.Attributes.observeAttributeChange", Element, "indeterminate", "boolean", "", true); + } + } + + private bool _checked => CheckState ?? Value; + + private bool _indeterminate => ThreeState + ? !CheckState.HasValue + : !ShowIndeterminate && !CheckState.HasValue; + + private bool HasLabel() => !string.IsNullOrEmpty(Label) || LabelTemplate is not null; + + private async Task SetValueChangedAsync(bool newValue) + { + if (Value == newValue) + { + return; + } + + Value = newValue; + + if (ValueChanged.HasDelegate) + { + await ValueChanged.InvokeAsync(newValue); + } + } + + private async Task SetCheckStateChangedAsync(bool? newValue) + { + if (CheckState == newValue) + { + return; + } + + CheckState = newValue; + + if (newValue is null) + { + await SetValueChangedAsync(newValue: false); + } + else + { + await SetValueChangedAsync(newValue.Value); + } + + if (CheckStateChanged.HasDelegate) + { + await CheckStateChanged.InvokeAsync(newValue); + } + } + + private async Task OnCheckChangedHandlerAsync(ChangeEventArgs e) + { + ArgumentNullException.ThrowIfNull(e); + + if (ThreeState) + { + if (_checked) + { + // Current Check + if (ThreeStateOrderUncheckToIntermediate) + { + await SetToUncheckedAsync(); + } + else + { + await SetToIndeterminateAsync(); + } + } + else if (_indeterminate) + { + // Current _indeterminate + if (ThreeStateOrderUncheckToIntermediate) + { + await SetToCheckedAsync(); + } + else + { + await SetToUncheckedAsync(); + } + } + else + { + // Current Uncheck + if (ThreeStateOrderUncheckToIntermediate && ShowIndeterminate) + { + await SetToIndeterminateAsync(); + } + else + { + await SetToCheckedAsync(); + } + } + } + else + { + await SetCheckStateChangedAsync(!_checked); + } + } + + private async Task SetToIndeterminateAsync() + { + await SetCheckStateChangedAsync(ShowIndeterminate ? null : false); + } + + private async Task SetToCheckedAsync() + { + await SetCheckStateChangedAsync(true); + } + + private async Task SetToUncheckedAsync() + { + await SetCheckStateChangedAsync(newValue: false); + } +} diff --git a/src/Core/Enums/CheckboxShape.cs b/src/Core/Enums/CheckboxShape.cs new file mode 100644 index 000000000..aed04084f --- /dev/null +++ b/src/Core/Enums/CheckboxShape.cs @@ -0,0 +1,25 @@ +// ------------------------------------------------------------------------ +// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------ + +using System.ComponentModel; + +namespace Microsoft.FluentUI.AspNetCore.Components; + +/// +/// The visual appearance of the . +/// +public enum CheckboxShape +{ + /// + /// The default appearance. The border is square. + /// + [Description("square")] + Square, + + /// + /// The appearance where the border is circular. + /// + [Description("circular")] + Circular, +} diff --git a/src/Core/Enums/CheckboxSize.cs b/src/Core/Enums/CheckboxSize.cs new file mode 100644 index 000000000..71af64b5e --- /dev/null +++ b/src/Core/Enums/CheckboxSize.cs @@ -0,0 +1,25 @@ +// ------------------------------------------------------------------------ +// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------ + +using System.ComponentModel; + +namespace Microsoft.FluentUI.AspNetCore.Components; + +/// +/// Indicates the size of the . +/// +public enum CheckboxSize +{ + /// + /// Medium size. + /// + [Description("medium")] + Medium, + + /// + /// Large size. + /// + [Description("large")] + Large, +} diff --git a/tests/Core/Components.Tests.csproj b/tests/Core/Components.Tests.csproj index 0f0354a9b..a75115bb4 100644 --- a/tests/Core/Components.Tests.csproj +++ b/tests/Core/Components.Tests.csproj @@ -1,4 +1,4 @@ - + net9.0 diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Default.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Default.verified.razor.html new file mode 100644 index 000000000..1b430e18b --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Default.verified.razor.html @@ -0,0 +1,2 @@ + + diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_LabelTemplate.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_LabelTemplate.verified.razor.html new file mode 100644 index 000000000..e83b67c4b --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_LabelTemplate.verified.razor.html @@ -0,0 +1,6 @@ + + + + diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html new file mode 100644 index 000000000..0fc8f18f4 --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html @@ -0,0 +1,4 @@ + +
Start
+
End
+
diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor new file mode 100644 index 000000000..1bdd30850 --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor @@ -0,0 +1,184 @@ +@using Microsoft.FluentUI.AspNetCore.Components.Extensions +@using Microsoft.FluentUI.AspNetCore.Components.Tests.Extensions +@using Microsoft.FluentUI.AspNetCore.Components.Utilities +@using System.ComponentModel.DataAnnotations +@using Xunit; +@inherits TestContext + +@code +{ + public FluentCheckboxTests() + { + JSInterop.Mode = JSRuntimeMode.Loose; + Services.AddFluentUIComponents(); + } + + [Fact] + public void FluentCheckbox_Default() + { + // Arrange && Act + var cut = Render(@ + ); + + // Assert + cut.Verify(); + } + + [Theory] + [InlineData(CheckboxShape.Circular, "circular")] + [InlineData(CheckboxShape.Square, "square")] + public void FluentCheckbox_Shape(CheckboxShape checkboxShape, string expectedAttribute) + { + // Arrange && Act + var cut = Render(@); + + // Assert + cut.MarkupMatches($""); + } + + [Theory] + [InlineData(CheckboxSize.Medium, "medium")] + [InlineData(CheckboxSize.Large, "large")] + public void FluentCheckbox_Size(CheckboxSize checkboxSize, string expectedAttribute) + { + // Arrange && Act + var cut = Render(@); + + // Assert + cut.MarkupMatches($""); + } + + [Fact] + public void FluentCheckbox_EnableThreeState() + { + // Arrange && Act + var cut = Render(@); + + // Assert + cut.MarkupMatches($""); + } + + [Fact] + public void FluentCheckbox_ShowIndeterminate() + { + // Arrange && Act + var cut = Render(@); + + // Assert + cut.MarkupMatches($""); + } + + [Fact] + public void FluentCheckbox_LabelTemplate() + { + // Arrange && Act + var cut = Render( + @ + ); + // Assert + cut.Verify(); + } + + [Fact] + public void FluentCheckbox_StartEndTemplate() + { + // Arrange && Act + var cut = Render( + @ + Start + End + + ); + + // Assert + cut.Verify(); + } + + [Theory] + [InlineData(true, true)] + [InlineData(false, false)] + public void FluentCheckbox_InitialValue_WithCheckState(bool? initialValue, bool expectedValue) + { + // Arrange && Act + bool value = default; + bool? checkState = initialValue; + var cut = Render( + @ + ); + + // Assert + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(false, true)] + [InlineData(true, false)] + public void FluentCheckbox_OnClick(bool initialValue, bool expectedValue) + { + // Arrange + var value = initialValue; + var cut = Render(@); + + // Act + cut.Find("fluent-checkbox").Change(""); + + // Assert + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(false, false, true)] + [InlineData(false, true, null)] + [InlineData(false, null, false)] + [InlineData(true, false, null)] + [InlineData(true, null, true)] + [InlineData(true, true, false)] + public void FluentCheckbox_ThreeStateOnClick(bool threeStateOrderUncheckToIntermediate, bool? initialValue, bool? expectedValue) + { + // Arrange + var value = initialValue; + var cut = Render(@); + var findCut = cut.Find("fluent-checkbox"); + + // Act + findCut.Change(""); + + // Assert + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(false, false, true)] + [InlineData(false, true, false)] + [InlineData(false, null, false)] + [InlineData(true, false, true)] + [InlineData(true, null, true)] + [InlineData(true, true, false)] + public void FluentCheckbox_ThreeStateWithoutIndeterminateStateOnClick(bool threeStateOrderUncheckToIntermediate, bool? initialValue, bool? expectedValue) + { + // Arrange + var value = initialValue; + var cut = Render(@); + var findCut = cut.Find("fluent-checkbox"); + + // Act + findCut.Change(""); + + // Assert + Assert.Equal(expectedValue, value); + } + + [Theory] + [InlineData(FluentInputAppearance.Filled, TextInputAppearance.FilledDarker)] + [InlineData(FluentInputAppearance.Outline, TextInputAppearance.Outline)] + [InlineData((FluentInputAppearance)999, TextInputAppearance.Outline)] + public void FluentCheckbox_ToTextInputAppearance(FluentInputAppearance appearance, TextInputAppearance expected) + { + var value = AspNetCore.Components.Migration.FluentInputAppearanceExtensions.ToTextInputAppearance(appearance); + + Assert.Equal(expected, value); + } +} From 71520117990f57cb9444797a599f682864942da8 Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Mon, 10 Feb 2025 18:27:18 +0100 Subject: [PATCH 2/8] WIP --- .../Examples/CheckboxAppearances.razor | 4 +- .../Checkbox/Examples/CheckboxDefault.razor | 11 +++ .../Checkbox/Examples/CheckboxDisabled.razor | 4 +- .../Examples/CheckboxIndeterminate.razor | 16 +--- .../Checkbox/Examples/CheckboxSizes.razor | 21 ----- .../Examples/CheckboxThreeStates.razor | 84 ++++++++----------- .../Components/Checkbox/FluentCheckbox.md | 10 +-- .../Components/Checkbox/FluentCheckbox.razor | 14 +--- .../Checkbox/FluentCheckbox.razor.cs | 9 +- 9 files changed, 67 insertions(+), 106 deletions(-) create mode 100644 examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDefault.razor delete mode 100644 examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxSizes.razor diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxAppearances.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxAppearances.razor index 3ccd52fc9..f9eba35eb 100644 --- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxAppearances.razor +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxAppearances.razor @@ -1,4 +1,4 @@ -
+ -
+ diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDefault.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDefault.razor new file mode 100644 index 000000000..35e5ee612 --- /dev/null +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDefault.razor @@ -0,0 +1,11 @@ + + + + + + +@code { + bool value1 = true; + bool value2 = true; + bool value3; +} diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDisabled.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDisabled.razor index 99b18f0db..c6b39f2c2 100644 --- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDisabled.razor +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxDisabled.razor @@ -1,7 +1,7 @@ -
+ -
+ diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxIndeterminate.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxIndeterminate.razor index f6959c350..e4f4294a9 100644 --- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxIndeterminate.razor +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxIndeterminate.razor @@ -1,18 +1,10 @@ -
+ -
-

Value is @(value ? "checked" : "unchecked")

-

CheckState is @(checkState is null ? "(null indeterminate)" : checkState.Value ? "checked" : "unchecked")

- - Display current values in console -
+ Value is @(value ? "checked" : "unchecked") + CheckState is @(checkState is null ? "(null indeterminate)" : checkState.Value ? "checked" : "unchecked") + @code { private bool value; private bool? checkState; - - void OnClick(){ - var checkStateString = checkState is null ? "(null indeterminate)" : checkState.Value.ToString(); - Console.WriteLine($"Value: {value} - Checkstate : {checkStateString}"); - } } diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxSizes.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxSizes.razor deleted file mode 100644 index dcbf6a4d4..000000000 --- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxSizes.razor +++ /dev/null @@ -1,21 +0,0 @@ -
- - - - - - - - - -
- - - - - - - - - -
diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxThreeStates.razor b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxThreeStates.razor index 86b54fdef..352c9296f 100644 --- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxThreeStates.razor +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/Examples/CheckboxThreeStates.razor @@ -1,52 +1,40 @@ -
- - - Value = @value1 - CheckState is @(checkState1 is null ? "(null indeterminate)" : checkState1.Value.ToString()) -
+ + + + + + + Value = @value1 - CheckState is @(state1 is null ? "(null indeterminate)" : state1.Value.ToString()) + - - - Value = @value2 -
- - - Value: @valueThreeStateOrderUncheckToIntermediate - CheckState is - @(checkStateThreeStateOrderUncheckToIntermediate is null - ? "(null indeterminate)" - : checkStateThreeStateOrderUncheckToIntermediate.Value.ToString()) - -
-
- - - Value: @valueShowIntermediateWithoutThreeState - CheckState is - @(checkStateShowIntermediateWithoutThreeState is null - ? "(null indeterminate)" - : checkStateShowIntermediateWithoutThreeState.Value.ToString()) -
+ + + + + + Value = @value2 + + + + + + + + Value = @value3 - CheckState is @(state3 is null ? "(null indeterminate)" : state3.Value.ToString()) + + + @code { - private bool? checkState1 = true; - private bool value1 = false; - private bool value2 = false; - private bool? checkStateThreeStateOrderUncheckToIntermediate = false; - private bool valueThreeStateOrderUncheckToIntermediate = false; - private bool valueShowIntermediateWithoutThreeState = false; - private bool? checkStateShowIntermediateWithoutThreeState = null; + bool value1, value2, value3; + bool? state1 = false, state3 = null; } diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md index 1e3bf0cde..cd3e87b29 100644 --- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md @@ -8,6 +8,8 @@ route: /Checkbox A **FluentCheckbox** component enables a user to select or deselect an option. It's typically used to capture a boolean value. +{{ CheckboxDefault }} + ## Appearance The apparent style of a checkbox can be changed by setting the `Shape` property, but also by setting the `Size` property. @@ -19,14 +21,6 @@ We recommend using a spacing of 24px between checkboxes and other components. {{ CheckboxAppearances }} -### Size - -The size of the checkbox can be adjusted using the `Size` property. The available sizes are: - -- `Medium`: The default size. -- `Large`: A larger size for the checkbox. - -{{ CheckboxSizes }} ### Indeterminate diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor b/src/Core/Components/Checkbox/FluentCheckbox.razor index 34c163e6c..32d17b336 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor @@ -2,15 +2,7 @@ @using Microsoft.FluentUI.AspNetCore.Components.Extensions @inherits FluentInputBase - - @if (HasLabel()) - { - - } - + } - + diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs index 75f2e090b..c3a124296 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs @@ -15,8 +15,15 @@ namespace Microsoft.FluentUI.AspNetCore.Components; /// public partial class FluentCheckbox : FluentInputBase, IFluentComponentElementBase { + /// + /// + /// + public FluentCheckbox() + { + LabelPosition = Components.LabelPosition.After; + } - /// + /// [Parameter] public ElementReference Element { get; set; } From 203c563274d283da2a00ea4fab7a99a3ff35d9bc Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Mon, 10 Feb 2025 18:38:23 +0100 Subject: [PATCH 3/8] Improve FluentCheckbox docs and error handling Updated FluentCheckbox.md to clarify indeterminate state usage, including `CheckState` property and `ShowIndeterminate` attribute. Changed error handling in FluentCheckbox.razor.cs to throw `ArgumentOutOfRangeException` for invalid boolean values. Simplified `SetCheckStateChangedAsync` method using null-coalescing operator. --- .../Components/Checkbox/FluentCheckbox.md | 12 +++++++++--- src/Core/Components/Checkbox/FluentCheckbox.razor.cs | 12 ++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md index cd3e87b29..b6f834558 100644 --- a/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md +++ b/examples/Demo/FluentUI.Demo.Client/Documentation/Components/Checkbox/FluentCheckbox.md @@ -24,10 +24,16 @@ We recommend using a spacing of 24px between checkboxes and other components. ### Indeterminate -The `FluentCheckbox` component supports an indeterminate state, which can be useful for scenarios where a checkbox represents a mixed or partial selection. -The indeterminate state is visually distinct from the checked and unchecked states. +To define the indeterminate state, you need to use the CheckState bindable property, +which has three possible values: null, true and false. -To set the checkbox to the indeterminate state, use the `Indeterminate` property. +For the majority of uses, a checkbox with two values (checked/unchecked) is probably sufficient. +In this case, the value bindable property is used. +Value has only two possible values: true and false. + +A ShowIndeterminate=‘true’ attribute allows you to indicate that the user cannot display this "Indeterminate" +state himself. This allows you to place the box in the indeterminate state when the page is first displayed, but +without being able to return to it afterwards (except by code). {{ CheckboxIndeterminate }} diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs index c3a124296..53f4011e5 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs @@ -120,8 +120,7 @@ protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(fa } result = default; - validationErrorMessage = "The provided value is not a valid boolean."; - return false; + throw new ArgumentOutOfRangeException("The provided value is not a valid boolean."); } /// @@ -166,14 +165,7 @@ private async Task SetCheckStateChangedAsync(bool? newValue) CheckState = newValue; - if (newValue is null) - { - await SetValueChangedAsync(newValue: false); - } - else - { - await SetValueChangedAsync(newValue.Value); - } + await SetValueChangedAsync(newValue ?? false); if (CheckStateChanged.HasDelegate) { From b952932defe3fac9131b83f4fce1ba6e48ad3b83 Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Mon, 10 Feb 2025 18:49:42 +0100 Subject: [PATCH 4/8] Fix unit testing --- Microsoft.FluentUI-v5.lutconfig | 6 +++ .../Components/Checkbox/FluentCheckbox.razor | 9 +++-- .../Checkbox/FluentCheckbox.razor.cs | 12 ++++++ ...FluentCheckbox_Default.verified.razor.html | 6 ++- ...Checkbox_LabelTemplate.verified.razor.html | 9 +++-- ...ckbox_StartEndTemplate.verified.razor.html | 11 ++++-- .../Checkbox/FluentCheckboxTests.razor | 38 ++++++++++++++----- 7 files changed, 67 insertions(+), 24 deletions(-) create mode 100644 Microsoft.FluentUI-v5.lutconfig diff --git a/Microsoft.FluentUI-v5.lutconfig b/Microsoft.FluentUI-v5.lutconfig new file mode 100644 index 000000000..596a86030 --- /dev/null +++ b/Microsoft.FluentUI-v5.lutconfig @@ -0,0 +1,6 @@ + + + true + true + 180000 + \ No newline at end of file diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor b/src/Core/Components/Checkbox/FluentCheckbox.razor index 32d17b336..e1a9fc414 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor @@ -3,18 +3,19 @@ @inherits FluentInputBase - diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs index 53f4011e5..3fc08b0ae 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection.Metadata; using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Web; using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; @@ -86,6 +87,17 @@ public FluentCheckbox() [Parameter] public CheckboxSize Size { get; set; } = CheckboxSize.Medium; + /// + /// Handler for the OnFocus event. + /// + /// + /// + protected virtual Task FocusOutHandlerAsync(FocusEventArgs e) + { + FocusLost = true; + return Task.CompletedTask; + } + /// protected override async Task OnInitializedAsync() { diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Default.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Default.verified.razor.html index 1b430e18b..f6f8a4f21 100644 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Default.verified.razor.html +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Default.verified.razor.html @@ -1,2 +1,4 @@ - - + + + + diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_LabelTemplate.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_LabelTemplate.verified.razor.html index e83b67c4b..83164377e 100644 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_LabelTemplate.verified.razor.html +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_LabelTemplate.verified.razor.html @@ -1,6 +1,7 @@ - - - + + + diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html index 0fc8f18f4..65fe81080 100644 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html @@ -1,4 +1,7 @@ - -
Start
-
End
-
+ + +
Start
+
End
+
+ +
diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor index 1bdd30850..56203749d 100644 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor @@ -22,7 +22,6 @@ Name="MyName" Value="true" /> ); - // Assert cut.Verify(); } @@ -33,10 +32,15 @@ public void FluentCheckbox_Shape(CheckboxShape checkboxShape, string expectedAttribute) { // Arrange && Act - var cut = Render(@); + var cut = Render(@); // Assert - cut.MarkupMatches($""); + cut.MarkupMatches($@" + + + + + "); } [Theory] @@ -45,30 +49,44 @@ public void FluentCheckbox_Size(CheckboxSize checkboxSize, string expectedAttribute) { // Arrange && Act - var cut = Render(@); + var cut = Render(@); // Assert - cut.MarkupMatches($""); + cut.MarkupMatches($@" + + + + "); } [Fact] public void FluentCheckbox_EnableThreeState() { // Arrange && Act - var cut = Render(@); + var cut = Render(@); // Assert - cut.MarkupMatches($""); + cut.MarkupMatches($@" + + + + + "); } [Fact] public void FluentCheckbox_ShowIndeterminate() { // Arrange && Act - var cut = Render(@); + var cut = Render(@); // Assert - cut.MarkupMatches($""); + cut.MarkupMatches($@" + + + + + "); } [Fact] @@ -87,7 +105,7 @@ { // Arrange && Act var cut = Render( - @ + @ Start End From 094f4a45c86be51c985195e0faedfb33765925a2 Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Mon, 10 Feb 2025 22:14:18 +0100 Subject: [PATCH 5/8] Enhance FluentCheckbox with new properties and methods Updated FluentCheckbox component: - Added properties: CheckState (nullable bool), Shape (default: Square), Size (default: Medium) - Removed and redefined properties: Shape, CheckState, Size - Added TryParseValueFromString method throwing NotSupportedException - Added InternalTryParseValueFromString method for parsing logic - Updated SetCheckStateChangedAsync to remove equality check Testing updates: - Added FluentCheckbox_TryParseValueFromString test - Refactored existing tests for readability and maintainability - Removed unused test FluentCheckbox_ToTextInputAppearance --- .../Checkbox/FluentCheckbox.razor.cs | 91 ++++++++----------- .../Checkbox/FluentCheckboxTests.razor | 33 ++++--- 2 files changed, 54 insertions(+), 70 deletions(-) diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs index 3fc08b0ae..da5129239 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs @@ -6,7 +6,6 @@ using System.Reflection.Metadata; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; -using Microsoft.FluentUI.AspNetCore.Components.Utilities; using Microsoft.JSInterop; namespace Microsoft.FluentUI.AspNetCore.Components; @@ -28,6 +27,25 @@ public FluentCheckbox() [Parameter] public ElementReference Element { get; set; } + /// + /// Gets or sets the state of the CheckBox: true, false or null. + /// Useful when the mode ThreeState is enable + /// + [Parameter] + public bool? CheckState { get; set; } + + /// + /// Gets or sets the shape of the checkbox + /// + [Parameter] + public CheckboxShape Shape { get; set; } = CheckboxShape.Square; + + /// + /// Gets or sets the size of the checkbox. See + /// + [Parameter] + public CheckboxSize Size { get; set; } = CheckboxSize.Medium; + /// /// Gets or sets a value indicating whether the user can display the indeterminate state by clicking the CheckBox. /// @@ -50,12 +68,6 @@ public FluentCheckbox() [Parameter] public bool ThreeStateOrderUncheckToIntermediate { get; set; } - /// - /// Gets or sets the shape of the checkbox - /// - [Parameter] - public CheckboxShape Shape { get; set; } = CheckboxShape.Square; - /// /// Gets or sets the content to prefix the input component. /// @@ -68,25 +80,12 @@ public FluentCheckbox() [Parameter] public virtual RenderFragment? EndTemplate { get; set; } - /// - /// Gets or sets the state of the CheckBox: true, false or null. - /// Useful when the mode ThreeState is enable - /// - [Parameter] - public bool? CheckState { get; set; } - /// /// Action to be called when the CheckBox state changes. /// [Parameter] public EventCallback CheckStateChanged { get; set; } - /// - /// Gets or sets the size of the checkbox. See - /// - [Parameter] - public CheckboxSize Size { get; set; } = CheckboxSize.Medium; - /// /// Handler for the OnFocus event. /// @@ -103,38 +102,12 @@ protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - if (HasLabel()) - { - // When the id is not provided, generate a unique id. This allow to use the label for. - Id ??= $"{Identifier.NewId()}"; - } - if (ThreeState && CheckState.HasValue) { await SetValueChangedAsync(CheckState.Value); } } - /// - /// Parses a string to create the . - /// - /// The string value to be parsed. - /// The result to inject into the Value. - /// If the value could not be parsed, provides a validation error message. - /// True if the value could be parsed; otherwise false. - protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out bool result, [NotNullWhen(false)] out string? validationErrorMessage) - { - if (bool.TryParse(value, out var parsedValue)) - { - result = parsedValue; - validationErrorMessage = null; - return true; - } - - result = default; - throw new ArgumentOutOfRangeException("The provided value is not a valid boolean."); - } - /// protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -151,8 +124,6 @@ protected override async Task OnAfterRenderAsync(bool firstRender) ? !CheckState.HasValue : !ShowIndeterminate && !CheckState.HasValue; - private bool HasLabel() => !string.IsNullOrEmpty(Label) || LabelTemplate is not null; - private async Task SetValueChangedAsync(bool newValue) { if (Value == newValue) @@ -170,11 +141,6 @@ private async Task SetValueChangedAsync(bool newValue) private async Task SetCheckStateChangedAsync(bool? newValue) { - if (CheckState == newValue) - { - return; - } - CheckState = newValue; await SetValueChangedAsync(newValue ?? false); @@ -248,4 +214,23 @@ private async Task SetToUncheckedAsync() { await SetCheckStateChangedAsync(newValue: false); } + + /// + /// Parses a string to create the . + /// + /// The string value to be parsed. + /// The result to inject into the Value. + /// If the value could not be parsed, provides a validation error message. + /// True if the value could be parsed; otherwise false. + protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out bool result, [NotNullWhen(false)] out string? validationErrorMessage) + { + // Overriding mandatory because the parent method is abstract and called via the OnChanged. + // However, this method is not used in this component because we need to manage the CheckState. + throw new NotSupportedException(); + } + + internal bool InternalTryParseValueFromString(string? value, [MaybeNullWhen(false)] out bool result, [NotNullWhen(false)] out string? validationErrorMessage) + { + return TryParseValueFromString(value, out result, out validationErrorMessage); + } } diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor index 56203749d..b7ee48f93 100644 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor @@ -74,6 +74,16 @@ "); } + [Fact] + public void FluentCheckbox_TryParseValueFromString() + { + // Arrange + var fluentCheckbox = new FluentCheckbox(); + + // Act & Assert + Assert.Throws(() => fluentCheckbox.InternalTryParseValueFromString(string.Empty, out var parsedValue, out var validationErrorMessage)); + } + [Fact] public void FluentCheckbox_ShowIndeterminate() { @@ -95,7 +105,7 @@ // Arrange && Act var cut = Render( @ - ); + ); // Assert cut.Verify(); } @@ -105,11 +115,11 @@ { // Arrange && Act var cut = Render( - @ - Start - End - - ); + @ + Start + End + + ); // Assert cut.Verify(); @@ -188,15 +198,4 @@ // Assert Assert.Equal(expectedValue, value); } - - [Theory] - [InlineData(FluentInputAppearance.Filled, TextInputAppearance.FilledDarker)] - [InlineData(FluentInputAppearance.Outline, TextInputAppearance.Outline)] - [InlineData((FluentInputAppearance)999, TextInputAppearance.Outline)] - public void FluentCheckbox_ToTextInputAppearance(FluentInputAppearance appearance, TextInputAppearance expected) - { - var value = AspNetCore.Components.Migration.FluentInputAppearanceExtensions.ToTextInputAppearance(appearance); - - Assert.Equal(expected, value); - } } From 6d26c09ada6ea14c5adb1dcebb5af6ce1d4c4b85 Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Tue, 11 Feb 2025 10:23:37 +0100 Subject: [PATCH 6/8] Refactor FluentCheckbox tests to use Verify method Replaced `cut.MarkupMatches` with `cut.Verify` in `FluentCheckboxTests.razor` to simplify assertions. Updated verified HTML files to include expected markup for various `FluentCheckbox` configurations, ensuring accurate comparisons in tests. --- ...ckbox_EnableThreeState.verified.razor.html | 5 ++++ ...heckbox_Shape-circular.verified.razor.html | 5 ++++ ...tCheckbox_Shape-square.verified.razor.html | 5 ++++ ...kbox_ShowIndeterminate.verified.razor.html | 5 ++++ ...entCheckbox_Size-large.verified.razor.html | 4 +++ ...ntCheckbox_Size-medium.verified.razor.html | 4 +++ .../Checkbox/FluentCheckboxTests.razor | 27 +++---------------- 7 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_EnableThreeState.verified.razor.html create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Shape-circular.verified.razor.html create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Shape-square.verified.razor.html create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_ShowIndeterminate.verified.razor.html create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Size-large.verified.razor.html create mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Size-medium.verified.razor.html diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_EnableThreeState.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_EnableThreeState.verified.razor.html new file mode 100644 index 000000000..856f7036b --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_EnableThreeState.verified.razor.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Shape-circular.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Shape-circular.verified.razor.html new file mode 100644 index 000000000..f6330a921 --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Shape-circular.verified.razor.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Shape-square.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Shape-square.verified.razor.html new file mode 100644 index 000000000..567c2e958 --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Shape-square.verified.razor.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_ShowIndeterminate.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_ShowIndeterminate.verified.razor.html new file mode 100644 index 000000000..856f7036b --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_ShowIndeterminate.verified.razor.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Size-large.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Size-large.verified.razor.html new file mode 100644 index 000000000..054d85bc4 --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Size-large.verified.razor.html @@ -0,0 +1,4 @@ + + + + diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Size-medium.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Size-medium.verified.razor.html new file mode 100644 index 000000000..136af48fc --- /dev/null +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_Size-medium.verified.razor.html @@ -0,0 +1,4 @@ + + + + diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor index b7ee48f93..98a2ad5c3 100644 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor @@ -35,12 +35,7 @@ var cut = Render(@); // Assert - cut.MarkupMatches($@" - - - - - "); + cut.Verify(suffix: expectedAttribute); } [Theory] @@ -52,11 +47,7 @@ var cut = Render(@); // Assert - cut.MarkupMatches($@" - - - - "); + cut.Verify(suffix: expectedAttribute); } [Fact] @@ -66,12 +57,7 @@ var cut = Render(@); // Assert - cut.MarkupMatches($@" - - - - - "); + cut.Verify(); } [Fact] @@ -91,12 +77,7 @@ var cut = Render(@); // Assert - cut.MarkupMatches($@" - - - - - "); + cut.Verify(); } [Fact] From c4636f1726614d148f8f6e16f7a0e0389b40a1a1 Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Tue, 11 Feb 2025 11:08:33 +0100 Subject: [PATCH 7/8] Remove StartTemplate and EndTemplate from FluentCheckbox This commit removes the `StartTemplate` and `EndTemplate` parameters and their associated rendering logic from the `FluentCheckbox` component. Changes include: - Deleting the `StartTemplate` and `EndTemplate` properties from `FluentCheckbox.razor.cs`. - Removing the conditional rendering of `StartTemplate` and `EndTemplate` in `FluentCheckbox.razor`. - Updating the closing tag of the `fluent-checkbox` element to be self-closing. - Removing the test case `FluentCheckbox_StartEndTemplate` from `FluentCheckboxTests.razor`. - Deleting the corresponding verification HTML for the removed test case in `FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html`. --- .../Components/Checkbox/FluentCheckbox.razor | 17 +---------------- .../Components/Checkbox/FluentCheckbox.razor.cs | 12 ------------ ...heckbox_StartEndTemplate.verified.razor.html | 7 ------- .../Checkbox/FluentCheckboxTests.razor | 17 +---------------- 4 files changed, 2 insertions(+), 51 deletions(-) delete mode 100644 tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor b/src/Core/Components/Checkbox/FluentCheckbox.razor index e1a9fc414..e8f2dc6b1 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor @@ -18,20 +18,5 @@ @onfocusout="@FocusOutHandlerAsync" @attributes="AdditionalAttributes" @onchange="@OnCheckChangedHandlerAsync" - slot="input"> - - @if (StartTemplate != null) - { -
- @StartTemplate -
- } - - @if (EndTemplate != null) - { -
- @EndTemplate -
- } -
+ slot="input" />
diff --git a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs index da5129239..4056fb103 100644 --- a/src/Core/Components/Checkbox/FluentCheckbox.razor.cs +++ b/src/Core/Components/Checkbox/FluentCheckbox.razor.cs @@ -68,18 +68,6 @@ public FluentCheckbox() [Parameter] public bool ThreeStateOrderUncheckToIntermediate { get; set; } - /// - /// Gets or sets the content to prefix the input component. - /// - [Parameter] - public virtual RenderFragment? StartTemplate { get; set; } - - /// - /// Gets or sets the content to suffix the input component. - /// - [Parameter] - public virtual RenderFragment? EndTemplate { get; set; } - /// /// Action to be called when the CheckBox state changes. /// diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html b/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html deleted file mode 100644 index 65fe81080..000000000 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.FluentCheckbox_StartEndTemplate.verified.razor.html +++ /dev/null @@ -1,7 +0,0 @@ - - -
Start
-
End
-
- -
diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor index 98a2ad5c3..3e5d96198 100644 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor @@ -89,22 +89,7 @@ ); // Assert cut.Verify(); - } - - [Fact] - public void FluentCheckbox_StartEndTemplate() - { - // Arrange && Act - var cut = Render( - @ - Start - End - - ); - - // Assert - cut.Verify(); - } + } [Theory] [InlineData(true, true)] From 70b1e6f40d6dd06aca2d9dae7b46f19a188ed399 Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Tue, 11 Feb 2025 12:25:54 +0100 Subject: [PATCH 8/8] Update FluentCheckbox tests and elements for Blazor Replaced with + diff --git a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor index 3e5d96198..d1f5b495e 100644 --- a/tests/Core/Components/Checkbox/FluentCheckboxTests.razor +++ b/tests/Core/Components/Checkbox/FluentCheckboxTests.razor @@ -85,8 +85,9 @@ { // Arrange && Act var cut = Render( - @ - ); + @ + ); + // Assert cut.Verify(); }