LeptonX 5.3.0 Custom-Select Overlay Drops <optgroup> and Breaks Inside <Modal> (Blazor Server)
Summary
LeptonX's custom-select overlay (the Bootstrap-style <button class="select-button"> /
<ul class="dropdown-menu"> overlay that LeptonX paints over every native <select> /
Blazorise <Select> on the page) has two distinct rendering bugs that surface in a
Blazor Server app running ABP 10.3.0 + LeptonX 5.3.0 + Blazorise 2.0.4:
<optgroup>headers are dropped — when a Blazorise<Select>contains<SelectGroup>(or a native<select>contains<optgroup>), the overlay flattens all options into a single ungrouped list. The group labels are gone and the user cannot tell which entry belongs to which group.- Options never render when the
<Select>is inside a<Modal>— the overlay button paints (showing the placeholder text) but the dropdown panel is empty when opened, no matter how many<SelectItem>s the underlying<select>has. This appears specific to Blazorise modals, where the<select>element is moved/wrapped after LeptonX has run its initial overlay pass.
Environment
| Component | Version |
| --- | --- |
| .NET | 10.0 |
| ABP Framework | 10.3.0 |
| Volo.Abp.AspNetCore.Components.Server.LeptonXTheme | 5.3.0 |
| Volo.Abp.AspNetCore.Components.Web.LeptonXTheme | 5.3.0 (transitive) |
| Volo.Abp.AspNetCore.Mvc.UI.Theme.LeptonX | 5.3.0 |
| Blazorise | 2.0.4 |
| LeptonX layout | LeptonXBlazorLayouts.SideMenu |
| Browser | Chrome (latest), reproduced in Edge |
Confirmed on LeptonX 5.3.0 with Blazorise 2.0.4 (our 10.3 baseline), so the bugs are LeptonX-side, not Blazorise.
Bug 1 — <optgroup> headers dropped
Reproduction
- ABP Blazor Server 10.3 with the LeptonX
SideMenulayout. - Add a Blazorise
<Select>outside any modal with grouped options, e.g.:
<Select TValue="string" @bind-Value="@SelectedLayoutCode">
<SelectItem Value="@string.Empty">Select report or layout</SelectItem>
@foreach (var reportType in ReportCollection)
{
<SelectGroup Label="@reportType.TypeName">
@foreach (var reportLayout in reportType.ReportLayoutList ?? [])
{
<SelectItem Value="@reportLayout.LayoutCode">@reportLayout.LayoutName</SelectItem>
}
</SelectGroup>
}
</Select>
- Run the app and open the dropdown.
Expected (and what the underlying <select> actually emits):
[Select report or layout]
─── A/R Aging ───
AR_Aging_Standard
AR_Aging_Detailed
─── Sales ───
Sales_Order_Open
Sales_Order_Closed
Actual: the overlay strips the group rows; the user sees a flat list:
[Select report or layout]
AR_Aging_Standard
AR_Aging_Detailed
Sales_Order_Open
Sales_Order_Closed
Verification — the underlying DOM is correct
Inspect <select> in DevTools and the <optgroup label="…"> elements are
present and well-formed. Native browser <select> rendering (when LeptonX's
overlay JS is removed) shows the groups correctly. Bug is in the overlay's
DOM walk; it iterates <option> only and ignores <optgroup>.
Affected page in our app
"Reports and Layouts" picker. Layouts are grouped by report type (e.g., A/R Aging, Sales). With the overlay, every layout name appears flattened with no indication of which report family it belongs to.
Bug 2 — <Select> content empty when hosted inside <Modal>
Reproduction
- Same project as above.
- Place a Blazorise
<Select>inside a Blazorise<Modal>:
<Modal @ref="MyModal" Centered="true">
<ModalContent>
<ModalBody>
<Select TValue="string" @bind-Value="@SelectedCurrency">
<SelectItem Value="">Select currency</SelectItem>
@foreach (var c in AvailableCurrencies)
{
<SelectItem Value="@c.Code">@c.Code - @c.Name</SelectItem>
}
</Select>
</ModalBody>
</ModalContent>
</Modal>
- Open the modal and click the select.
Expected: the dropdown panel lists every currency.
Actual: the overlay button shows the placeholder ("Select currency"),
but the dropdown panel is empty — no <li> rows are rendered, even though
the underlying <select> is fully populated.
Verification
- Inspect the
<select>in DevTools while the modal is open: every<option>is present and has the rightvalue/text content. - Submit the form by typing in the (functioning) overlay search input or
by pressing Tab and arrow keys — keyboard navigation also fails because
the overlay's
<ul>is empty. - Replace the Blazorise
<Select>with a native<select class="form-control">in the same modal: the overlay still paints, but its<ul>is now populated correctly. This is the smoking gun: the overlay successfully consumes a static native<select>but fails when the<select>is the one Blazorise rendered. Our hypothesis is that LeptonX runs its overlay-decoration pass once on modal open, before Blazorise has finished rendering the<select>'s<option>children, and never re-runs after Blazorise's second render pass.
Workaround
Replace Blazorise <Select> with native <select class="form-control">
elements (with explicit @onchange handlers, since @bind does not work
the same on native <select> for non-string values). The native <select>
keeps its native behaviour underneath the overlay, and either renders
correctly (groups visible) or, when LeptonX's overlay still mis-renders,
the native control behind it remains usable as a fallback.
Bug 1 workaround (groups)
@* Native <select> — LeptonX's custom-select overlay ignores <optgroup> *@
<select class="form-control" value="@Filter.LayoutCode" @onchange="OnLayoutCodeChanged" disabled="@IsLoading">
<option value="">@(IsLoading ? L["LoadingWithThreeDot"] : L["SelectReportOrLayout"])</option>
@foreach (var reportType in ReportCollection)
{
<optgroup label="@reportType.TypeName">
@foreach (var reportLayout in reportType.ReportLayoutList ?? [])
{
<option value="@reportLayout.LayoutCode">@reportLayout.LayoutName</option>
}
</optgroup>
}
</select>
private async Task OnLayoutCodeChanged(ChangeEventArgs e)
{
var layoutCode = e.Value?.ToString() ?? string.Empty;
Filter.LayoutCode = layoutCode;
await LoadLayoutParametersAsync(layoutCode);
}
Bug 2 workaround (modal)
@* Native <select> — LeptonX's custom-select overlay breaks inside modals *@
<select class="form-control" value="@NewSyncJob.BaseCurrency" @onchange="OnBaseCurrencyChanged" disabled="@(!AvailableCurrencies.Any())">
<option value="">@L["SelectCurrency"]</option>
@foreach (var currency in AvailableCurrencies)
{
<option value="@currency.Code">@currency.Code - @currency.Name</option>
}
</select>
private void OnBaseCurrencyChanged(ChangeEventArgs e)
{
NewSyncJob.BaseCurrency = e.Value?.ToString() ?? string.Empty;
}
For nullable enums in modals (e.g., DayOfWeek?), the value round-trips as
the integer string:
<select class="form-control" value="@((int?)NewSyncJob.SyncDayOfWeek)" @onchange="OnDayOfWeekChanged" disabled="@(NewSyncJob.Frequency != SyncFrequency.Weekly)">
<option value="">@L["SelectDay"]</option>
@foreach (var day in Enum.GetValues<DayOfWeek>())
{
<option value="@((int)day)">@L[$"Enum:DayOfWeek.{day}"]</option>
}
</select>
private void OnDayOfWeekChanged(ChangeEventArgs e)
{
var val = e.Value?.ToString();
NewSyncJob.SyncDayOfWeek = string.IsNullOrEmpty(val) ? null : (DayOfWeek)int.Parse(val);
}
4 Answer(s)
-
0
[AI-Bot] said:
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
theres no content
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Please contact us at https://abp.io/contact.
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
Thanks for the detailed report and the reproduction steps.
We reviewed the scenario with ABP 10.3.0, LeptonX 5.3.0, and Blazorise 2.0.4. This matches an issue in the LeptonX custom select overlay rather than a problem with your data binding.
In the affected LeptonX version, the custom overlay is applied to
select.form-selectcontrols and builds its own visible option list from the native<select>. This causes two problems in the scenarios you described:<optgroup>labels are not preserved in the custom overlay, so grouped options are displayed as a flat list.- In the Blazorise modal scenario, the native
<select>can contain the correct<option>elements while the custom overlay list remains empty or out of sync after the modal/component render cycle.
We are tracking this in Lepton issue
#3488, and the fix is being handled in PR#3491. The fix is targeted for the LeptonX5.2patch line and will be available with a5.2.xpatch package after the PR is merged and released.You can track issues and releases from here: abp.io/pro-releases
Until the fix is available in a LeptonX package release, the safest workaround is to avoid the LeptonX custom select overlay for the affected controls:
- Use a native
<select>that does not use theform-selectclass for grouped selects and modal-hosted selects, for exampleclass="form-control"as you already tried. - Keep explicit
@onchangehandlers for non-string or nullable enum values, because the native select value is received as a string. - Scope the workaround only to selects that use
<optgroup>or are rendered inside the affected modal flow.
Your DOM verification is also the right way to confirm the issue: if DevTools shows the expected
<option>/<optgroup>nodes in the native select but the LeptonX dropdown is flattened or empty, the problem is in the custom overlay rendering/synchronization layer.We will update this ticket when the LeptonX
5.2.xpatch package is available. If you still see an empty dropdown with a native select that does not useform-select, please share a minimal component/page that reproduces it, because that would indicate a separate rendering path.
Since this was confirmed as a product issue in LeptonX, we have refunded your support ticket credit.
Best regards,
ABP Support Team
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)