diff --git a/src/SimpleBlazorMultiselect.Demo/Layout/NavMenu.razor b/src/SimpleBlazorMultiselect.Demo/Layout/NavMenu.razor index c4440a6..c663fe3 100644 --- a/src/SimpleBlazorMultiselect.Demo/Layout/NavMenu.razor +++ b/src/SimpleBlazorMultiselect.Demo/Layout/NavMenu.razor @@ -76,5 +76,12 @@ ObjectBinding + + + \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect.Demo/Pages/DisabledDropdown.razor b/src/SimpleBlazorMultiselect.Demo/Pages/DisabledDropdown.razor new file mode 100644 index 0000000..d89891c --- /dev/null +++ b/src/SimpleBlazorMultiselect.Demo/Pages/DisabledDropdown.razor @@ -0,0 +1,31 @@ +@page "/DisabledDropdown" +

DisabledDropdown

+ +
+
+ +
+
+ +
+ You have selected the following items: + +
+
+ +@code { + private bool _isDisabled; + + private HashSet _selectedItems = []; +} \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect.Tests/DisabledTests.cs b/src/SimpleBlazorMultiselect.Tests/DisabledTests.cs new file mode 100644 index 0000000..3c3bad6 --- /dev/null +++ b/src/SimpleBlazorMultiselect.Tests/DisabledTests.cs @@ -0,0 +1,100 @@ +using Bunit; +using FluentAssertions; +using Xunit; + +namespace SimpleBlazorMultiselect.Tests; + +public class DisabledTests : BaseTest +{ + [Fact] + public void Component_WhenDisabled_HasDisabledAttribute() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Disabled, true) + ); + + var button = component.Find("button"); + button.HasAttribute("disabled").Should().BeTrue(); + } + + [Fact] + public void Component_WhenDisabled_DoesNotOpenOnClick() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Disabled, true) + ); + + var button = component.Find("button"); + button.Click(); + + AssertClosed(component); + } + + [Fact] + public void Component_WhenEnabled_OpensOnClick() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Disabled, false) + ); + + var button = component.Find("button"); + button.Click(); + + AssertOpen(component, TestOptions.Count); + } + + [Fact] + public void Component_WhenDisabledThenEnabled_OpensOnClick() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Disabled, true) + ); + + var button = component.Find("button"); + button.Click(); + AssertClosed(component); + + component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, false)); + + button.Click(); + AssertOpen(component, TestOptions.Count); + } + + [Fact] + public void Component_WhenEnabledThenDisabled_DoesNotOpenOnClick() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Disabled, false) + ); + + var button = component.Find("button"); + button.Click(); + AssertOpen(component, TestOptions.Count); + + component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true)); + + button.Click(); + AssertClosed(component); + } + + [Fact] + public void Component_WhenDisabledWithMenuOpen_ClosesMenu() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + ); + + var button = component.Find("button"); + button.Click(); + AssertOpen(component, TestOptions.Count); + + component.SetParametersAndRender(parameters => parameters.Add(p => p.Disabled, true)); + + AssertClosed(component); + } +} \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect.Tests/EqualityTests.cs b/src/SimpleBlazorMultiselect.Tests/EqualityTests.cs new file mode 100644 index 0000000..eace961 --- /dev/null +++ b/src/SimpleBlazorMultiselect.Tests/EqualityTests.cs @@ -0,0 +1,170 @@ +using AngleSharp.Dom; +using AngleSharp.Html.Dom; +using Bunit; +using FluentAssertions; +using Microsoft.AspNetCore.Components; +using Xunit; + +namespace SimpleBlazorMultiselect.Tests; + +public class EqualityTests : BaseTest +{ + [Fact] + public void Component_CanDeselect_WhenPrefilledValueItems() + { + var options = new List + { + new("1", "Apple"), + new("2", "Banana"), + new("3", "Cherry") + }; + var selectedItems = new HashSet + { + new("1", "Apple") + }; + + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, options) + .Add(p => p.SelectedOptions, selectedItems) + .Add(p => p.StringSelector, item => item.Name) + .Add(p => p.DefaultText, "Select fruits") + .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedItems = newSelection; }))); + + var button = component.Find("button"); + button.TextContent.Should().Contain("Apple"); + button.Click(); + + // Now only apple should be checked + var appleOption = component.FindAll(".dropdown-item")[0]; + var appleCheckbox = appleOption.QuerySelector("input[type='checkbox']"); + appleCheckbox.Should().NotBeNull(); + appleCheckbox.IsChecked.Should().BeTrue(); + + appleOption.Click(); + + // After clicking, apple should be deselected + selectedItems.Should().BeEmpty(); + button = component.Find("button"); + button.TextContent.Should().Be("Select fruits"); + } + + [Fact] + public void Component_CanDeselect_WhenPrefilledReferenceItems() + { + var options = new List + { + new("1", "Apple"), + new("2", "Banana"), + new("3", "Cherry") + }; + var selectedItems = new HashSet + { + new("1", "Apple") + }; + + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, options) + .Add(p => p.SelectedOptions, selectedItems) + .Add(p => p.StringSelector, item => item.Name) + .Add(p => p.DefaultText, "Select fruits") + .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedItems = newSelection; }))); + + var button = component.Find("button"); + button.TextContent.Should().Contain("Apple"); + button.Click(); + + // Now only apple should be checked + var appleOption = component.FindAll(".dropdown-item")[0]; + var appleCheckbox = appleOption.QuerySelector("input[type='checkbox']"); + appleCheckbox.Should().NotBeNull(); + appleCheckbox.IsChecked.Should().BeTrue(); + + appleOption.Click(); + + // After clicking, apple should be deselected + selectedItems.Should().BeEmpty(); + button = component.Find("button"); + button.TextContent.Should().Be("Select fruits"); + } + + [Fact] + public void Component_CanDeselectValueItem_WhenMatchByReference() + { + var options = new List + { + new("1", "Apple"), + new("2", "Banana"), + new("3", "Cherry") + }; + var selectedItems = new HashSet + { + new("1", "Apple") + }; + + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, options) + .Add(p => p.SelectedOptions, selectedItems) + .Add(p => p.StringSelector, item => item.Name) + .Add(p => p.DefaultText, "Select fruits") + .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedItems = newSelection; })) + .Add(p => p.MatchByReference, true)); // Should not matter for value types + + var button = component.Find("button"); + button.TextContent.Should().Contain("Apple"); + button.Click(); + + // Now only apple should be checked + var appleOption = component.FindAll(".dropdown-item")[0]; + var appleCheckbox = appleOption.QuerySelector("input[type='checkbox']"); + appleCheckbox.Should().NotBeNull(); + appleCheckbox.IsChecked.Should().BeTrue(); + + appleOption.Click(); + + // After clicking, apple should be deselected + selectedItems.Should().BeEmpty(); + button = component.Find("button"); + button.TextContent.Should().Be("Select fruits"); + } + + [Fact] + public void Component_CannotDeselectIdenticalInstance_WhenMatchByReference() + { + var options = new List + { + new("1", "Apple"), + new("2", "Banana"), + new("3", "Cherry") + }; + var selectedItems = new HashSet + { + new("1", "Apple") + }; + + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, options) + .Add(p => p.SelectedOptions, selectedItems) + .Add(p => p.StringSelector, item => item.Name) + .Add(p => p.DefaultText, "Select fruits") + .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedItems = newSelection; })) + .Add(p => p.MatchByReference, true)); // This will break the deselection + + var button = component.Find("button"); + button.TextContent.Should().Contain("Apple"); + button.Click(); + + // Apple should not be checked because the instance is different + // So clicking it will add another apple instead of removing the existing one + var appleOption = component.FindAll(".dropdown-item")[0]; + var appleCheckbox = appleOption.QuerySelector("input[type='checkbox']"); + appleCheckbox.Should().NotBeNull(); + appleCheckbox.IsChecked.Should().BeFalse(); + + appleOption.Click(); + + // After clicking, we should have two apples + selectedItems.Should().HaveCount(2); + button = component.Find("button"); + button.TextContent.Should().Be("Apple, Apple"); + } +} \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect.Tests/Helper/BaseTest.cs b/src/SimpleBlazorMultiselect.Tests/Helper/BaseTest.cs new file mode 100644 index 0000000..2d2e7e6 --- /dev/null +++ b/src/SimpleBlazorMultiselect.Tests/Helper/BaseTest.cs @@ -0,0 +1,44 @@ +using Bunit; +using FluentAssertions; + +namespace SimpleBlazorMultiselect.Tests; + +public class BaseTest : TestContext +{ + protected readonly List TestOptions = + [ + "Apple", + "Banana", + "Cherry", + "Date", + "Elderberry" + ]; + + public BaseTest() + { + JSInterop.SetupModule("./_content/SimpleBlazorMultiselect/js/simpleMultiselect.js") + .SetupModule("register", invocation => invocation.Arguments.Count == 2) + .SetupVoid("dispose"); + } + + protected static void AssertOpen(IRenderedComponent> component, int? expectedItemCount = null) + { + var dropdown = component.Find(".dropdown-menu.show"); + dropdown.Should().NotBeNull(); + + if (expectedItemCount.HasValue) + { + var dropdownItems = component.FindAll(".dropdown-item"); + dropdownItems.Should().HaveCount(expectedItemCount.Value); + } + } + + protected static void AssertClosed(IRenderedComponent> component) + { + var dropdown = component.FindAll(".dropdown-menu.show"); + dropdown.Should().BeEmpty(); + + var dropdownItems = component.FindAll(".dropdown-item"); + dropdownItems.Should().HaveCount(0); + } +} \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect.Tests/Helper/TestReferenceItem.cs b/src/SimpleBlazorMultiselect.Tests/Helper/TestReferenceItem.cs new file mode 100644 index 0000000..1bcc1bd --- /dev/null +++ b/src/SimpleBlazorMultiselect.Tests/Helper/TestReferenceItem.cs @@ -0,0 +1,7 @@ +namespace SimpleBlazorMultiselect.Tests; + +public class TestReferenceItem(string id, string name) +{ + public string Id { get; set; } = id; + public string Name { get; set; } = name; +} \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect.Tests/Helper/TestValueItem.cs b/src/SimpleBlazorMultiselect.Tests/Helper/TestValueItem.cs new file mode 100644 index 0000000..525be2e --- /dev/null +++ b/src/SimpleBlazorMultiselect.Tests/Helper/TestValueItem.cs @@ -0,0 +1,3 @@ +namespace SimpleBlazorMultiselect.Tests; + +public record TestValueItem(string Id, string Name); \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect.Tests/SimpleBlazorMultiselect.Tests.csproj b/src/SimpleBlazorMultiselect.Tests/SimpleBlazorMultiselect.Tests.csproj index a0b8d9d..b49f0c3 100644 --- a/src/SimpleBlazorMultiselect.Tests/SimpleBlazorMultiselect.Tests.csproj +++ b/src/SimpleBlazorMultiselect.Tests/SimpleBlazorMultiselect.Tests.csproj @@ -19,6 +19,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/SimpleBlazorMultiselect.Tests/SimpleMultiselectTests.cs b/src/SimpleBlazorMultiselect.Tests/SimpleMultiselectTests.cs index eeca66a..faf215b 100644 --- a/src/SimpleBlazorMultiselect.Tests/SimpleMultiselectTests.cs +++ b/src/SimpleBlazorMultiselect.Tests/SimpleMultiselectTests.cs @@ -3,27 +3,17 @@ using Bunit; using FluentAssertions; using Microsoft.AspNetCore.Components; -using Microsoft.JSInterop; using Xunit; namespace SimpleBlazorMultiselect.Tests; -public class SimpleMultiselectTests : TestContext +public class SimpleMultiselectTests : BaseTest { - private readonly List _testOptions = new() { "Apple", "Banana", "Cherry", "Date", "Elderberry" }; - - public SimpleMultiselectTests() - { - JSInterop.SetupModule("./_content/SimpleBlazorMultiselect/js/simpleMultiselect.js") - .SetupModule("register", invocation => invocation.Arguments.Count == 2) - .SetupVoid("dispose"); - } - [Fact] public void Component_RendersWithDefaultText_WhenNoOptionsSelected() { var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.DefaultText, "Choose items")); var button = component.Find("button"); @@ -36,7 +26,7 @@ public void Component_RendersSelectedOptions_WhenOptionsAreSelected() var selectedOptions = new HashSet { "Apple", "Banana" }; var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.SelectedOptions, selectedOptions)); var button = component.Find("button"); @@ -47,28 +37,27 @@ public void Component_RendersSelectedOptions_WhenOptionsAreSelected() public void Component_TogglesDropdown_WhenButtonClicked() { var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions)); + .Add(p => p.Options, TestOptions)); var button = component.Find("button"); button.Click(); - var dropdown = component.Find(".dropdown-menu.show"); - dropdown.Should().NotBeNull(); + AssertOpen(component); } [Fact] public void Component_ShowsAllOptions_WhenDropdownIsOpen() { var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions)); + .Add(p => p.Options, TestOptions)); var button = component.Find("button"); button.Click(); var dropdownItems = component.FindAll(".dropdown-item"); - dropdownItems.Should().HaveCount(_testOptions.Count); + dropdownItems.Should().HaveCount(TestOptions.Count); - foreach (var option in _testOptions) + foreach (var option in TestOptions) { component.Markup.Should().Contain(option); } @@ -79,7 +68,7 @@ public void Component_SelectsOption_WhenOptionClicked() { var selectedOptions = new HashSet(); var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.SelectedOptions, selectedOptions) .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedOptions = newSelection; }))); @@ -101,7 +90,7 @@ public void Component_DeselectsOption_WhenSelectedOptionClicked() { var selectedOptions = new HashSet { "Apple" }; var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.SelectedOptions, selectedOptions) .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedOptions = newSelection; }))); @@ -123,7 +112,7 @@ public void Component_DeselectsOption_WhenSelectedOptionClicked() public void Component_ShowsFilterInput_WhenCanFilterIsTrue() { var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.CanFilter, true)); var button = component.Find("button"); @@ -138,7 +127,7 @@ public void Component_ShowsFilterInput_WhenCanFilterIsTrue() public void Component_FiltersOptions_WhenFilterTextEntered() { var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.CanFilter, true)); var button = component.Find("button"); @@ -176,7 +165,7 @@ public void Component_UsesCustomStringSelector_WhenProvided() public void Component_UsesCustomFilterPredicate_WhenProvided() { var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.CanFilter, true) .Add(p => p.FilterPredicate, (item, filter) => item.StartsWith(filter, StringComparison.OrdinalIgnoreCase))); @@ -209,7 +198,7 @@ public void Component_SingleSelectMode_SelectsOnlyOneOption() { var selectedOptions = new HashSet(); var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.SelectedOptions, selectedOptions) .Add(p => p.IsMultiSelect, false) .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedOptions = newSelection; }))); @@ -233,55 +222,11 @@ public void Component_SingleSelectMode_SelectsOnlyOneOption() selectedOptions.Should().NotContain("Apple"); } - [Fact] - public void Component_AppliesCustomCssClasses_WhenProvided() - { - var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) - .Add(p => p.Class, "custom-class")); - - var container = component.Find(".simple-dropdown"); - container.ClassList.Should().Contain("custom-class"); - } - - [Fact] - public void Component_AppliesCustomStyles_WhenProvided() - { - var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) - .Add(p => p.Style, "width: 300px;")); - - var container = component.Find(".simple-dropdown"); - container.GetAttribute("style").Should().Contain("width: 300px;"); - } - - [Fact] - public void Component_SetsButtonId_WhenProvided() - { - var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) - .Add(p => p.Id, "test-multiselect")); - - var button = component.Find("button"); - button.Id.Should().Be("test-multiselect"); - } - - [Fact] - public void Component_ShowsStandaloneStyles_WhenStandaloneIsTrue() - { - var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) - .Add(p => p.Standalone, true)); - - var container = component.Find(".simple-dropdown"); - container.ClassList.Should().Contain("simple-bs-compat"); - } - [Fact] public void Component_CachesFilteredOptions_ForPerformance() { var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, _testOptions) + .Add(p => p.Options, TestOptions) .Add(p => p.CanFilter, true)); var button = component.Find("button"); @@ -298,171 +243,4 @@ public void Component_CachesFilteredOptions_ForPerformance() dropdownItems.Should().HaveCount(1); dropdownItems[0].TextContent.Should().Contain("Apple"); } - - [Fact] - public void Component_CanDeselect_WhenPrefilledValueItems() - { - var options = new List - { - new("1", "Apple"), - new("2", "Banana"), - new("3", "Cherry") - }; - var selectedItems = new HashSet - { - new("1", "Apple") - }; - - var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, options) - .Add(p => p.SelectedOptions, selectedItems) - .Add(p => p.StringSelector, item => item.Name) - .Add(p => p.DefaultText, "Select fruits") - .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedItems = newSelection; }))); - - var button = component.Find("button"); - button.TextContent.Should().Contain("Apple"); - button.Click(); - - // Now only apple should be checked - var appleOption = component.FindAll(".dropdown-item")[0]; - var appleCheckbox = appleOption.QuerySelector("input[type='checkbox']"); - appleCheckbox.Should().NotBeNull(); - appleCheckbox.IsChecked.Should().BeTrue(); - - appleOption.Click(); - - // After clicking, apple should be deselected - selectedItems.Should().BeEmpty(); - button = component.Find("button"); - button.TextContent.Should().Be("Select fruits"); - } - - [Fact] - public void Component_CanDeselect_WhenPrefilledReferenceItems() - { - var options = new List - { - new("1", "Apple"), - new("2", "Banana"), - new("3", "Cherry") - }; - var selectedItems = new HashSet - { - new("1", "Apple") - }; - - var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, options) - .Add(p => p.SelectedOptions, selectedItems) - .Add(p => p.StringSelector, item => item.Name) - .Add(p => p.DefaultText, "Select fruits") - .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedItems = newSelection; }))); - - var button = component.Find("button"); - button.TextContent.Should().Contain("Apple"); - button.Click(); - - // Now only apple should be checked - var appleOption = component.FindAll(".dropdown-item")[0]; - var appleCheckbox = appleOption.QuerySelector("input[type='checkbox']"); - appleCheckbox.Should().NotBeNull(); - appleCheckbox.IsChecked.Should().BeTrue(); - - appleOption.Click(); - - // After clicking, apple should be deselected - selectedItems.Should().BeEmpty(); - button = component.Find("button"); - button.TextContent.Should().Be("Select fruits"); - } - - [Fact] - public void Component_CanDeselectValueItem_WhenMatchByReference() - { - var options = new List - { - new("1", "Apple"), - new("2", "Banana"), - new("3", "Cherry") - }; - var selectedItems = new HashSet - { - new("1", "Apple") - }; - - var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, options) - .Add(p => p.SelectedOptions, selectedItems) - .Add(p => p.StringSelector, item => item.Name) - .Add(p => p.DefaultText, "Select fruits") - .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedItems = newSelection; })) - .Add(p => p.MatchByReference, true)); // Should not matter for value types - - var button = component.Find("button"); - button.TextContent.Should().Contain("Apple"); - button.Click(); - - // Now only apple should be checked - var appleOption = component.FindAll(".dropdown-item")[0]; - var appleCheckbox = appleOption.QuerySelector("input[type='checkbox']"); - appleCheckbox.Should().NotBeNull(); - appleCheckbox.IsChecked.Should().BeTrue(); - - appleOption.Click(); - - // After clicking, apple should be deselected - selectedItems.Should().BeEmpty(); - button = component.Find("button"); - button.TextContent.Should().Be("Select fruits"); - } - - [Fact] - public void Component_CannotDeselectIdenticalInstance_WhenMatchByReference() - { - var options = new List - { - new("1", "Apple"), - new("2", "Banana"), - new("3", "Cherry") - }; - var selectedItems = new HashSet - { - new("1", "Apple") - }; - - var component = RenderComponent>(parameters => parameters - .Add(p => p.Options, options) - .Add(p => p.SelectedOptions, selectedItems) - .Add(p => p.StringSelector, item => item.Name) - .Add(p => p.DefaultText, "Select fruits") - .Add(p => p.SelectedOptionsChanged, EventCallback.Factory.Create>(this, newSelection => { selectedItems = newSelection; })) - .Add(p => p.MatchByReference, true)); // This will break the deselection - - var button = component.Find("button"); - button.TextContent.Should().Contain("Apple"); - button.Click(); - - // Apple should not be checked because the instance is different - // So clicking it will add another apple instead of removing the existing one - var appleOption = component.FindAll(".dropdown-item")[0]; - var appleCheckbox = appleOption.QuerySelector("input[type='checkbox']"); - appleCheckbox.Should().NotBeNull(); - appleCheckbox.IsChecked.Should().BeFalse(); - - appleOption.Click(); - - // After clicking, we should have two apples - selectedItems.Should().HaveCount(2); - button = component.Find("button"); - button.TextContent.Should().Be("Apple, Apple"); - } - - private record TestValueItem(string Id, string Name); - - private class TestReferenceItem(string id, string name) - { - public string Id { get; set; } = id; - public string Name { get; set; } = name; - } } \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect.Tests/StylingTests.cs b/src/SimpleBlazorMultiselect.Tests/StylingTests.cs new file mode 100644 index 0000000..fe3ff8c --- /dev/null +++ b/src/SimpleBlazorMultiselect.Tests/StylingTests.cs @@ -0,0 +1,52 @@ +using Bunit; +using FluentAssertions; +using Xunit; + +namespace SimpleBlazorMultiselect.Tests; + +public class StylingTests : BaseTest +{ + [Fact] + public void Component_AppliesCustomCssClasses_WhenProvided() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Class, "custom-class")); + + var container = component.Find(".simple-dropdown"); + container.ClassList.Should().Contain("custom-class"); + } + + [Fact] + public void Component_AppliesCustomStyles_WhenProvided() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Style, "width: 300px;")); + + var container = component.Find(".simple-dropdown"); + container.GetAttribute("style").Should().Contain("width: 300px;"); + } + + [Fact] + public void Component_SetsButtonId_WhenProvided() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Id, "test-multiselect")); + + var button = component.Find("button"); + button.Id.Should().Be("test-multiselect"); + } + + [Fact] + public void Component_ShowsStandaloneStyles_WhenStandaloneIsTrue() + { + var component = RenderComponent>(parameters => parameters + .Add(p => p.Options, TestOptions) + .Add(p => p.Standalone, true)); + + var container = component.Find(".simple-dropdown"); + container.ClassList.Should().Contain("simple-bs-compat"); + } +} \ No newline at end of file diff --git a/src/SimpleBlazorMultiselect/SimpleMultiselect.razor b/src/SimpleBlazorMultiselect/SimpleMultiselect.razor index 2638812..95d3d2c 100644 --- a/src/SimpleBlazorMultiselect/SimpleMultiselect.razor +++ b/src/SimpleBlazorMultiselect/SimpleMultiselect.razor @@ -9,7 +9,8 @@