\ 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:
+
+ @foreach (var item in _selectedItems)
+ {
+
@item
+ }
+
+
+
+
+@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 @@