Skip to content

Commit c538d73

Browse files
committed
fix(ListExtended): use ToStringFunc for ItemCollection search on complex types (#606)
Search with ItemCollection was ignoring ToStringFunc and falling back to Converter.Convert() for complex types, making search fail for custom display formats. - Use ToStringFunc when available for ItemCollection search - Add integration tests for complex type search - Update docs with complex type example
1 parent 0e5282e commit c538d73

4 files changed

Lines changed: 131 additions & 4 deletions

File tree

docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/SelectExtended/Examples/SelectExtendedExample6.razor

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,47 @@
2121
</MudItem>
2222
</MudGrid>
2323

24+
<MudGrid Class="mt-8">
25+
<MudItem xs="12">
26+
<MudText Typo="Typo.h6" Class="mb-4">Complex Type with ToStringFunc and Search</MudText>
27+
</MudItem>
28+
<MudItem xs="12" sm="8" Class="d-flex gap-4">
29+
<MudSelectExtended MultiSelection="true"
30+
ItemCollection="TestHouses"
31+
@bind-Value="_selectedHouse"
32+
SearchBox="true"
33+
T="TestHouse"
34+
Label="Houses (Complex Type)"
35+
ToStringFunc="@((TestHouse house) => $"{house.Number} - {house.Name}")"
36+
AnchorOrigin="Origin.BottomCenter"
37+
Variant="Variant.Outlined"
38+
HelperText="Search by number or name (e.g., '1 -' or 'Test3')"
39+
SearchBoxClearable="true" />
40+
</MudItem>
41+
42+
<MudItem xs="12" sm="4">
43+
<MudPaper Class="pa-4" Elevation="0" Outlined="true">
44+
<MudText Typo="Typo.subtitle2" Class="mb-2"><strong>Selected House:</strong></MudText>
45+
@if (_selectedHouse != null)
46+
{
47+
<MudText Typo="Typo.body2">Number: @_selectedHouse.Number</MudText>
48+
<MudText Typo="Typo.body2">Name: @_selectedHouse.Name</MudText>
49+
<MudText Typo="Typo.body2">Postcode: @_selectedHouse.Postcode</MudText>
50+
}
51+
else
52+
{
53+
<MudText Typo="Typo.body2" Color="Color.Secondary">None selected</MudText>
54+
}
55+
</MudPaper>
56+
</MudItem>
57+
</MudGrid>
58+
2459
@code {
2560
bool _multiselection;
2661
bool _searchBoxAutoFocus;
2762
bool _searchBoxClearable;
2863
private string? _selectedState;
64+
private TestHouse? _selectedHouse;
2965

3066
private string[] _states =
3167
{
@@ -45,6 +81,22 @@
4581
"Washington", "West Virginia", "Wisconsin", "Wyoming",
4682
};
4783

84+
private class TestHouse
85+
{
86+
public int Number { get; set; }
87+
public string? Name { get; set; }
88+
public string? Postcode { get; set; }
89+
}
90+
91+
private List<TestHouse> TestHouses = new()
92+
{
93+
new TestHouse { Number = 1, Name = "Test1", Postcode = "12345" },
94+
new TestHouse { Number = 2, Name = "Test2", Postcode = "54321" },
95+
new TestHouse { Number = 3, Name = "Test3", Postcode = "67890" },
96+
new TestHouse { Number = 4, Name = "Test4", Postcode = "09876" },
97+
new TestHouse { Number = 5, Name = "Test5", Postcode = "11223" },
98+
};
99+
48100
private bool SearchItems(string value, string searchString)
49101
{
50102
if (searchString == "")

src/CodeBeam.MudBlazor.Extensions/Components/ListExtended/MudListExtended.razor.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ protected internal async Task SearchBoxHandleKeyDownAsync(KeyboardEventArgs obj)
956956
await MudSelectExtended.FocusAsync();
957957
}
958958
break;
959-
case "Escape":
959+
case "Escape":
960960
if (MudSelectExtended != null && MultiSelection == false)
961961
{
962962
await MudSelectExtended.CloseMenu();
@@ -1783,10 +1783,14 @@ protected internal async ValueTask ScrollToMiddleAsync(MudListItemExtended<T?>?
17831783

17841784
if (SearchFunc != null)
17851785
{
1786-
return ItemCollection.Where(x => SearchFunc.Invoke(x, _searchString)).ToList();
1786+
return [.. ItemCollection.Where(x => SearchFunc.Invoke(x, _searchString))];
17871787
}
17881788

1789-
return ItemCollection.Where(x => Converter.Convert(x)?.Contains(_searchString, StringComparison.InvariantCultureIgnoreCase) == true).ToList();
1789+
var stringValue = new Func<T?, string?>(x =>
1790+
ToStringFunc != null ? ToStringFunc(x) : Converter.Convert(x)
1791+
);
1792+
1793+
return [.. ItemCollection.Where(x => stringValue(x)?.Contains(_searchString, StringComparison.InvariantCultureIgnoreCase) == true)];
17901794
}
17911795

17921796
/// <summary>
@@ -1834,7 +1838,7 @@ protected async Task OnDoubleClickHandler(MouseEventArgs? args, T? itemValue)
18341838
///
18351839
/// </summary>
18361840
protected internal MudListItemExtended<T?>? ActiveItem => _lastActivatedItem;
1837-
1841+
18381842
/// <summary>
18391843
///
18401844
/// </summary>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
@namespace MudExtensions.UnitTests.TestComponents
2+
3+
<MudListExtended @ref="List"
4+
SearchBox="true"
5+
ItemCollection="Houses"
6+
MultiSelection="true"
7+
ToStringFunc="@(house => $"{house!.Number} - {house.Name}")">
8+
</MudListExtended>
9+
10+
@code {
11+
public MudListExtended<TestHouse>? List { get; set; }
12+
13+
public List<TestHouse> Houses = new()
14+
{
15+
new TestHouse { Number = 1, Name = "Test1", Postcode = "12345" },
16+
new TestHouse { Number = 2, Name = "Test2", Postcode = "54321" },
17+
new TestHouse { Number = 3, Name = "Test3", Postcode = "67890" },
18+
};
19+
20+
public class TestHouse
21+
{
22+
public int Number { get; set; }
23+
public string? Name { get; set; }
24+
public string? Postcode { get; set; }
25+
}
26+
}

tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/ListExtendedTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,5 +363,50 @@ public async Task List_SearchChanged_WithNullItemCollection()
363363
var finalItems = comp.FindAll("div.mud-list-item-extended").Count;
364364
finalItems.Should().Be(9);
365365
}
366+
367+
368+
[Test]
369+
public void ListExtended_Search_With_ItemCollection_Should_Use_ToStringFunc()
370+
{
371+
// Arrange
372+
var comp = Context.Render<ListExtendedItemCollectionSearchTest>();
373+
var list = comp.FindComponent<MudListExtended<ListExtendedItemCollectionSearchTest.TestHouse>>().Instance;
374+
375+
list.SearchBox.Should().BeTrue();
376+
list.ItemCollection.Should().HaveCount(3);
377+
378+
// Act - search for "1 - Test1" using reflection to set _searchString
379+
var searchStringField = typeof(MudListExtended<ListExtendedItemCollectionSearchTest.TestHouse>)
380+
.GetField("_searchString", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
381+
searchStringField?.SetValue(list, "1 - Test1");
382+
383+
var result = list.GetSearchedItems();
384+
385+
// Assert - should find item by ToStringFunc format, not class name
386+
result.Should().NotBeNull();
387+
result.Should().HaveCount(1);
388+
result.First().Number.Should().Be(1);
389+
result.First().Name.Should().Be("Test1");
390+
}
391+
392+
[Test]
393+
public void ListExtended_Search_Should_Find_Items_By_Custom_Display_Format_Not_ClassName()
394+
{
395+
// Arrange
396+
var comp = Context.Render<ListExtendedItemCollectionSearchTest>();
397+
var list = comp.FindComponent<MudListExtended<ListExtendedItemCollectionSearchTest.TestHouse>>().Instance;
398+
399+
// Act - search for "Test2" (part of the formatted display, not the class name)
400+
var searchStringField = typeof(MudListExtended<ListExtendedItemCollectionSearchTest.TestHouse>)
401+
.GetField("_searchString", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
402+
searchStringField?.SetValue(list, "Test2");
403+
404+
var result = list.GetSearchedItems();
405+
406+
// Assert
407+
result.Should().HaveCount(1);
408+
result.First().Name.Should().Be("Test2");
409+
result.First().Number.Should().Be(2);
410+
}
366411
}
367412
}

0 commit comments

Comments
 (0)