Skip to content

Commit f10450b

Browse files
DiegoCardosoclaude
andauthored
fix: focus selected item with wrapped in-memory data providers (#9396)
A `ComboBox` with `setFocusSelectedItem(true)` throws a `ClassCastException` when opening the dropdown if its items come from a `ListDataProvider` wrapped with `withConvertedFilter(...)`. `scrollToSelectedItem()` resolved the selected item's index through the list data view, which casts the provider to `ListDataProvider`. The wrapper is in-memory but not a `ListDataProvider`, so the cast fails. ```java ComboBox<String> comboBox = new ComboBox<>(); ListDataProvider<String> dataProvider = DataProvider .ofCollection(List.of("A", "B", "C", "D")); comboBox.setItems(dataProvider.withConvertedFilter( filterText -> item -> item.contains(filterText))); comboBox.setFocusSelectedItem(true); comboBox.setValue("C"); comboBox.setOpened(true); // throws ClassCastException ``` `scrollToSelectedItem()` now resolves the index through the generic data view (`ComboBoxDataView`) for in-memory providers instead of the list data view. The generic data view reads items from the data communicator and works for any in-memory provider, wrapped or not, so the list data view is no longer used for providers that aren't a `ListDataProvider`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ab10f50 commit f10450b

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

  • vaadin-combo-box-flow-parent/vaadin-combo-box-flow/src

vaadin-combo-box-flow-parent/vaadin-combo-box-flow/src/main/java/com/vaadin/flow/component/combobox/ComboBox.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,12 @@ private void scrollToSelectedItem() {
422422
}
423423
Integer index;
424424
if (dataProvider.isInMemory()) {
425-
index = getListDataView().getItemIndex(getValue()).orElse(null);
425+
// Use the generic data view rather than the list data view: it
426+
// resolves the item index through the data communicator and works
427+
// for any in-memory provider, including wrapped ones (e.g. from
428+
// ListDataProvider.withConvertedFilter) that are not a
429+
// ListDataProvider and would fail the list data view's cast.
430+
index = getGenericDataView().getItemIndex(getValue()).orElse(null);
426431
} else {
427432
index = getLazyDataView().getItemIndex(getValue()).orElse(null);
428433
}

vaadin-combo-box-flow-parent/vaadin-combo-box-flow/src/test/java/com/vaadin/flow/component/combobox/ComboBoxTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.vaadin.flow.component.combobox;
1717

1818
import java.util.Arrays;
19+
import java.util.List;
1920
import java.util.stream.Stream;
2021

2122
import org.junit.jupiter.api.Assertions;
@@ -29,9 +30,12 @@
2930
import com.vaadin.flow.component.Tag;
3031
import com.vaadin.flow.component.Text;
3132
import com.vaadin.flow.component.Unit;
33+
import com.vaadin.flow.component.internal.PendingJavaScriptInvocation;
3234
import com.vaadin.flow.component.shared.HasThemeVariant;
3335
import com.vaadin.flow.component.shared.InputField;
3436
import com.vaadin.flow.data.binder.Binder;
37+
import com.vaadin.flow.data.provider.DataProvider;
38+
import com.vaadin.flow.data.provider.ListDataProvider;
3539
import com.vaadin.flow.di.Instantiator;
3640
import com.vaadin.flow.dom.Element;
3741

@@ -253,6 +257,31 @@ void setFocusSelectedItem_isFocusSelectedItem() {
253257
Assertions.assertFalse(comboBox.isFocusSelectedItem());
254258
}
255259

260+
@Test
261+
void focusSelectedItem_convertedFilterListDataProvider_scrollsToSelectedItem() {
262+
// withConvertedFilter wraps the ListDataProvider in a provider that is
263+
// in-memory but not a ListDataProvider. Opening the dropdown must
264+
// resolve the selected item's index without failing.
265+
ComboBox<String> comboBox = new ComboBox<>();
266+
ui.add(comboBox);
267+
ListDataProvider<String> dataProvider = DataProvider
268+
.ofCollection(List.of("A", "B", "C", "D"));
269+
comboBox.setItems(dataProvider.withConvertedFilter(
270+
filterText -> item -> item.contains(filterText)));
271+
comboBox.setFocusSelectedItem(true);
272+
comboBox.setValue("C");
273+
comboBox.setOpened(true);
274+
275+
var invocations = ui.dumpPendingJavaScriptInvocations().stream()
276+
.map(PendingJavaScriptInvocation::getInvocation)
277+
.filter(invocation -> invocation.getExpression()
278+
.contains("scrollToIndex"))
279+
.toList();
280+
Assertions.assertEquals(1, invocations.size());
281+
// Parameter 0 is the target element, parameter 1 is the item index
282+
Assertions.assertEquals(2, invocations.get(0).getParameters().get(1));
283+
}
284+
256285
@Test
257286
void setFilterTimeout_getFilterTimeout() {
258287
ComboBox<String> comboBox = new ComboBox<>();

0 commit comments

Comments
 (0)