[UI] Add DashboardLayout and WidgetPicker components - #1706
Conversation
Introduce a responsive DashboardLayout (sticky sidebar on desktop, bottom SwipeableDrawer on mobile) and a WidgetPicker UI with types for WidgetItem. Added files: src/custom/DashboardLayout/index.tsx, src/custom/WidgetPicker/WidgetPicker.tsx, src/custom/WidgetPicker/index.tsx. Updated exports in src/custom/index.ts, src/custom/index.tsx and root src/index.tsx to re-export the new components and their props/types so they are available from the package API. Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request introduces two new custom components: DashboardLayout, which provides a responsive layout with a sticky sidebar on desktop and a swipeable bottom drawer on mobile, and WidgetPicker, which displays a list of available widgets to add. The feedback highlights a critical state desync issue in DashboardLayout where the onClose callback is not triggered when the mobile drawer is closed. Additionally, improvements are suggested for WidgetPicker to enhance type safety by replacing an any type with a more specific type, and to correct a discrepancy in the JSDoc documentation regarding the default header background color.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Invoke the optional onClose callback when the mobile drawer is closed (in SwipeableDrawer.onClose and the header toggle). Remove an unused Drawer import. Improve WidgetPicker typings: use unknown for the extra properties index signature and change onAddWidget to accept the widget payload without its 'key'. Also update the headerBackgroundColor JSDoc default. These changes ensure proper close propagation and stronger type safety. Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
Remove the isSidebarOpen condition from the mobile SwipeableDrawer to ensure the drawer is always displayed on mobile devices, regardless of sidebar state. Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
Only expose the swipe area when the sidebar is open, and stop firing the layout `onClose` callback from mobile drawer toggles. This keeps mobile drawer state changes local while preserving the wider sidebar interaction area when appropriate. Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
Drops the unused `onClose` prop from `DashboardLayout`'s public props and implementation, simplifying the component API. Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
Katotodan
left a comment
There was a problem hiding this comment.
@NSTKrishna, thanks for the work.
Can you consider adding some accessibility features to clickable elements of this PR?
banana-three-join
left a comment
There was a problem hiding this comment.
Other than the requested changes, it looks good to me!
…ut-mobile-responsive
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughDashboardLayout, WidgetPicker, and WidgetEmptyState are added. PlainCard and ResponsiveDataTable use the shared empty-state component. Custom and package barrel exports expose the new components and their public types. ChangesCustom dashboard components
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Parent
participant DashboardLayout
participant BottomSheet
participant Fab
Parent->>DashboardLayout: pass sidebar state and content
DashboardLayout->>DashboardLayout: evaluate mobile breakpoint
DashboardLayout->>BottomSheet: render mobile sidebar
BottomSheet->>DashboardLayout: report dismissal
DashboardLayout->>Fab: show reopen control
Fab->>BottomSheet: reopen sidebar
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Introduces a new `WidgetEmptyState` component with optional icon and action button support, and exports it through both `custom` and root barrels. Refactors `DashboardLayout` into its own file with explicit type/value re-exports and updates mobile sidebar rendering so the drawer is only mounted when the sidebar is open. Also switches `WidgetPicker` to explicit named/type exports for a clearer public API surface. Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/custom/DashboardLayout/index.tsx`:
- Around line 104-110: Update the puller Box styling in DashboardLayout to
replace the hard-coded light/dark background colors with the semantic theme
token theme.palette.divider, preserving the existing dimensions and border
radius.
- Around line 82-102: The minimized drawer trigger in DashboardLayout must be
keyboard-operable and accessible. Replace the interactive Box behavior with a
focusable labelled button (or Sistent button) rendered outside the drawer when
minimized, reuse the existing toggle logic, and expose isMobileDrawerOpen
through the button’s expanded state.
In `@src/custom/index.tsx`:
- Line 193: Update the custom barrel in src/custom/index.tsx to re-export
DashboardLayout alongside WidgetPicker, using the existing DashboardLayout
export from the custom domain index; preserve the current exports and ensure the
symbol is available through the feature-local, domain, and root barrel chain.
In `@src/custom/WidgetPicker/WidgetPicker.tsx`:
- Around line 40-52: Update the `WidgetPicker` component’s `Box` `sx` prop to
compose the base styles and `containerSx` as an array, preserving callback and
array-based `SxProps` values instead of spreading `containerSx` into the base
object.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f6f90140-acf6-4123-bd39-b29179641844
📒 Files selected for processing (6)
src/custom/DashboardLayout/index.tsxsrc/custom/WidgetPicker/WidgetPicker.tsxsrc/custom/WidgetPicker/index.tsxsrc/custom/index.tssrc/custom/index.tsxsrc/index.tsx
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/custom/DashboardLayout/DashboardLayout.tsx`:
- Around line 25-36: Remove the unused onClose prop from DashboardLayoutProps,
delete its callback documentation, and stop destructuring it in DashboardLayout.
Keep the component’s internal close behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 361b392d-3dc7-4524-bcbb-7fac97080c43
📒 Files selected for processing (6)
src/custom/DashboardLayout/DashboardLayout.tsxsrc/custom/DashboardLayout/index.tsxsrc/custom/WidgetEmptyState/index.tsxsrc/custom/WidgetPicker/index.tsxsrc/custom/index.tssrc/index.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- src/custom/index.ts
- src/index.tsx
This updates several dashboard-related components for better UX and consistency: DashboardLayout drops an unused `onClose` prop, adds keyboard-accessible sidebar toggle behavior, and uses theme divider color for the drawer handle. PlainCard and ResponsiveDataTable now show `WidgetEmptyState` when content is empty (including table `noMatch` text), and WidgetPicker now merges `containerSx` safely by supporting array/object forms. It also exports `DashboardLayout` from the custom barrel. Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/custom/ResponsiveDataTable.tsx`:
- Around line 167-178: Update the options transformation around bodyTextLabels
and updatedOptions to always assign WidgetEmptyState for noMatch, using the
existing string value when provided and a fallback message otherwise. Build a
fresh body object with the resolved noMatch value instead of mutating
bodyTextLabels or any caller-owned options object.
- Around line 163-164: Update the textLabels declaration in ResponsiveDataTable
to use MUIDataTableOptions or a narrow local type whose body.noMatch accepts
string | React.ReactNode. Remove both any casts and the eslint suppression while
preserving the existing fallback to an empty labels object.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 0a9896c3-7d47-4e3c-a46f-b9137e960ed3
📒 Files selected for processing (5)
src/custom/DashboardLayout/DashboardLayout.tsxsrc/custom/DashboardWidgets/PlainCard.tsxsrc/custom/ResponsiveDataTable.tsxsrc/custom/WidgetPicker/WidgetPicker.tsxsrc/custom/index.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- src/custom/index.tsx
- src/custom/WidgetPicker/WidgetPicker.tsx
Use `MUIDataTableOptions` for `ResponsiveDataTableProps.options` instead of `object`, removing `any` casts around `textLabels`. The no-match handling now consistently renders `WidgetEmptyState`, using the provided `body.noMatch` string when available and falling back to `"No data available"`. Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
|
Once this is merged #1653 , we should replace the existing SwipeableDrawer in DashboardLayout with the new BottomSheet |
Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
- Import Fab from '../../base' instead of '@mui/material' (optional-peer guard) - Import SxProps/Theme as 'import type' in WidgetPicker (erased at runtime) - Replace useEffect state mirror with useRef-guarded two-dimension model: isSheetVisible is owned by DashboardLayout and resets to true only when isSidebarOpen transitions OFF→ON, allowing users to minimize the sheet while Edit Mode stays active (FAB appears as the re-open affordance) - Move WidgetEmptyState body from index.tsx to WidgetEmptyState.tsx; index.tsx becomes a clean re-export barrel (consistent with other components) - Remove duplicate WidgetPicker/DashboardLayout exports from index.tsx (already covered by index.ts) Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/custom/DashboardLayout/DashboardLayout.tsx`:
- Around line 69-75: Add a sidebarTitle prop to the surrounding DashboardLayout
component and pass it as the title prop to BottomSheet, ensuring the mobile
dialog receives an accessible name while preserving the existing visibility and
close behavior.
In `@src/custom/WidgetEmptyState/WidgetEmptyState.tsx`:
- Around line 27-29: Update WidgetEmptyState so the message alone is wrapped
with role="status" and aria-live="polite"; keep the optional icon and action
Button outside that live-region element while preserving their existing
rendering and behavior.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 47a5cca2-c87f-4280-8e42-178a12b2bdaf
📒 Files selected for processing (5)
src/custom/DashboardLayout/DashboardLayout.tsxsrc/custom/WidgetEmptyState/WidgetEmptyState.tsxsrc/custom/WidgetEmptyState/index.tsxsrc/custom/WidgetPicker/WidgetPicker.tsxsrc/index.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- src/custom/WidgetPicker/WidgetPicker.tsx
- src/index.tsx
- DashboardLayout: add sidebarTitle prop (default 'Widget Picker') and pass it to BottomSheet so the Dialog gets an aria-labelledby label; without a title the dialog is unlabelled for screen readers - WidgetEmptyState: narrow role=status + aria-live=polite to the message Typography only; the outer Box and Button siblings are removed from the live region to prevent AT from hiding or degrading interactive semantics Signed-off-by: NSTKrishna <krishnagehlot936@gmail.com>
Description
This PR introduces three new reusable components to
@sistent/sistentto support responsive, widget-based dashboard layouts across Meshery and Meshery Cloud, and structurally integrates a standardized empty state universally across both ecosystems.1. DashboardLayout
A layout wrapper that manages the main content area and a collapsible sidebar for widget configuration.
BottomSheet(Dialog-based) that slides up from the bottom.isSheetVisibleis owned byDashboardLayoutand is independent fromisSidebarOpen(Edit Mode). It resetstrueonly when Edit Mode transitions off→on, preventing desyncs. All MUI imports go through../../base(no direct@mui/materialimports).sidebarWidth,sidebarTopOffset, andsidebarHeightfor easy integration with different navigation structures.2. WidgetPicker
A component for displaying and adding widgets to a layout.
widgetsToAddand anonAddWidgetcallback, keeping it completely decoupled from domain logic.onClosecallback.3. WidgetEmptyState
A standardized centered empty-state component, now structurally embedded into Sistent's core data-display components.
role="status"+aria-live="polite"for screen reader compatibility.icon,message, andactionbutton.WidgetEmptyState, while strictly adhering toMUIDataTableOptionstyping (noMatch: string | React.ReactNode).resourcesarray is empty.Changes Made
src/custom/DashboardLayout/DashboardLayout.tsxandsrc/custom/DashboardLayout/index.tsx.src/custom/WidgetPicker/WidgetPicker.tsxandsrc/custom/WidgetPicker/index.tsx.src/custom/WidgetEmptyState/WidgetEmptyState.tsxwithindex.tsxas a clean re-export barrel.src/custom/index.tsandsrc/index.tsx(explicit root re-exports per therollup-plugin-dtsbarrel-drop requirement).WidgetEmptyStateintoResponsiveDataTable.tsxandPlainCard.tsx.Fabimported from../../base;SxProps<Theme>usesimport type; removed duplicate exports fromsrc/custom/index.tsx.useEffectstate mirror with auseRef-guarded two-dimension state model for the mobile sheet.Related Issues
mesheryandmeshery-clouddashboards and properly support mobile views.BottomSheetusage inDashboardLayoutwill be reviewed for alignment.Testing Performed
npm run build— clean, all three symbols (DashboardLayout,WidgetPicker,WidgetEmptyState) present indist/index.d.ts.npm run lint— no errors.optionalPeerDependenciesandpublishedTypeSurfaceDependenciesguards.node_moduleslocal link in bothmesheryandmeshery-clouddevelopment environments.WidgetEmptyStatewithout errors.Demo
Screen.Recording.2026-07-13.at.6.43.33.AM.mov
Screen.Recording.2026-07-13.at.10.29.58.AM.mov
Notes for Reviewers
This PR fixes #
Signed commits
Summary by CodeRabbit
New Features
Bug Fixes