Open michielpauw opened 2 months ago
Hey, thanks for pointing this out. There seems to be a small bug in the current implementation indeed.
In the end it comes down to whether the formElement (be it an input/field or an option) is focusable or not. For a listbox(but also for select-rich and combobox that use listbox under the hood), individual options are not focusable, only their "host" (the listbox component, that contains a tabindex) is. This is because listboxes are not implemented with roving tabindexes, but via aria-activedescendant.
Bottom line: LionOption
is not designed to receive focus, and therefore it has no _focusableNode
and that explains the undefined
error above.
We should be able to solve it like this:
- if (firstFormElWithError.formElements?.length > 0) {
+ if (hasFocusableChildren(firstFormElWithError)) {
function hasFocusableChildren(formEl) {
// this implies all children have the same type (either all of them are focusable or none of them are)
return formEl.formElements?.some(child => child._focusableNode);
}
Expected behavior
When nothing is selected, validation message appears, and focus is placed on Listbox component (or some other component).
Actual Behavior
Validation message does appear, but a console error is thrown:
Additional context
I suspect this PR introduced the issue: https://github.com/ing-bank/lion/commit/31e079d591ad7df2de3869e0435688ebd8c6af4f#diff-418f32cd1a1ebdd1843e3b43e66e105d1c946423f10086fd1d0d0830f1bbd903
At first,
firstFormElWithError
isListbox
, and I believe this should be the element to which the focus is set to. But because of this condition, the method gets called again:The children of
Listbox
(i.e. LionOptions) do not have anerror
, but still the first child becomesfirstFormElWithError
because of this:After this, the method tries to set the focus to the first Option, but this does not have a
_focusableNode
.I'm not 100% certain what the intended behaviour is, but I would argue that the check done in (2) should in some way be combined with the check done in (1), before re-calling the method.