Summary
Below the md breakpoint, DashboardLayout renders sidebarContent inside a BottomSheet and pairs it with a floating action button to reopen that sheet. But WidgetPicker draws its own close control (aria-label="Close widget picker") inside that content, and that control is wired to the consumer's onClose - the same callback that clears isSidebarOpen.
So on mobile there are two close affordances in the same sheet with different and irreconcilable outcomes:
- The sheet's own close clears
DashboardLayout's internal open state. isSidebarOpen stays true, so the FAB renders and the user can reopen the sheet. Correct.
WidgetPicker's X runs the consumer's onClose, which clears isSidebarOpen. That unmounts the sheet and the FAB together, so the picker cannot be reopened at all until the consumer's own state cycles.
The second one is a dead end the user cannot get out of from the UI.
Where
DashboardLayout, mobile branch (from the built 0.22.6 bundle, names shortened):
isSidebarOpen && isMobile && (
<>
<BottomSheet open={internalOpen} onClose={() => setInternalOpen(false)} ...>
{sidebarContent}
</BottomSheet>
{!internalOpen && <Fab onClick={() => setInternalOpen(true)} ... />}
</>
)
Both the sheet and the FAB are gated on isSidebarOpen, so anything that clears it takes the FAB with it. WidgetPicker's X is rendered inside sidebarContent, which is exactly what the sheet is displaying.
Why the consumer cannot fix this cleanly
A consumer could make its onClose breakpoint-aware - clear isSidebarOpen on desktop, do nothing on mobile - but that requires restating Sistent's md breakpoint in the consumer, so the panel's behaviour and the component's layout decision become two values that must agree and nothing checks. That is the class of bug we were already fixing in layer5io/meshery-cloud#5992, so we declined to introduce another instance of it and left the X consistent with desktop.
Suggested fix
WidgetPicker should not render its own close control when it is being displayed inside the bottom sheet - the sheet already provides one, and the two mean different things. Options, in rough order of preference:
DashboardLayout tells WidgetPicker it is embedded (a context value or an internal prop), and WidgetPicker omits its X in that case.
WidgetPicker takes an explicit prop to suppress the close control, and DashboardLayout sets it on the mobile branch.
DashboardLayout gates the FAB on something other than isSidebarOpen, so a consumer clearing that prop still leaves a way back.
Either of the first two keeps the breakpoint knowledge inside the component that already owns it.
Consumer status
Not blocking. layer5io/meshery-cloud#5992 separated panel visibility from its edit-session lifetime, so the X is no longer destructive there - it closes the panel and the edit session continues with its guards armed. On mobile the picker simply cannot be reopened until edit mode cycles, which is a degraded but non-destructive state, and better than the behaviour it replaced.
Related: #1843, #1844.
Summary
Below the
mdbreakpoint,DashboardLayoutrenderssidebarContentinside aBottomSheetand pairs it with a floating action button to reopen that sheet. ButWidgetPickerdraws its own close control (aria-label="Close widget picker") inside that content, and that control is wired to the consumer'sonClose- the same callback that clearsisSidebarOpen.So on mobile there are two close affordances in the same sheet with different and irreconcilable outcomes:
DashboardLayout's internal open state.isSidebarOpenstays true, so the FAB renders and the user can reopen the sheet. Correct.WidgetPicker's X runs the consumer'sonClose, which clearsisSidebarOpen. That unmounts the sheet and the FAB together, so the picker cannot be reopened at all until the consumer's own state cycles.The second one is a dead end the user cannot get out of from the UI.
Where
DashboardLayout, mobile branch (from the built0.22.6bundle, names shortened):Both the sheet and the FAB are gated on
isSidebarOpen, so anything that clears it takes the FAB with it.WidgetPicker's X is rendered insidesidebarContent, which is exactly what the sheet is displaying.Why the consumer cannot fix this cleanly
A consumer could make its
onClosebreakpoint-aware - clearisSidebarOpenon desktop, do nothing on mobile - but that requires restating Sistent'smdbreakpoint in the consumer, so the panel's behaviour and the component's layout decision become two values that must agree and nothing checks. That is the class of bug we were already fixing in layer5io/meshery-cloud#5992, so we declined to introduce another instance of it and left the X consistent with desktop.Suggested fix
WidgetPickershould not render its own close control when it is being displayed inside the bottom sheet - the sheet already provides one, and the two mean different things. Options, in rough order of preference:DashboardLayouttellsWidgetPickerit is embedded (a context value or an internal prop), andWidgetPickeromits its X in that case.WidgetPickertakes an explicit prop to suppress the close control, andDashboardLayoutsets it on the mobile branch.DashboardLayoutgates the FAB on something other thanisSidebarOpen, so a consumer clearing that prop still leaves a way back.Either of the first two keeps the breakpoint knowledge inside the component that already owns it.
Consumer status
Not blocking. layer5io/meshery-cloud#5992 separated panel visibility from its edit-session lifetime, so the X is no longer destructive there - it closes the panel and the edit session continues with its guards armed. On mobile the picker simply cannot be reopened until edit mode cycles, which is a degraded but non-destructive state, and better than the behaviour it replaced.
Related: #1843, #1844.