This is an automated email from the ASF dual-hosted git repository. rusackas pushed a commit to branch mobile-dashboard-support in repository https://gitbox.apache.org/repos/asf/superset.git
commit 43f170aed64e2dbce04e9ff0a74cdd791e2ec2ca Author: Evan Rusackas <[email protected]> AuthorDate: Fri Jan 9 11:42:23 2026 -0800 feat(mobile): improve mobile nav drawer UX - Hide main nav items on mobile, show hamburger menu in header - Simplify drawer: remove header, full-width items, no right border - Show only consumption items (Dashboards, Theme, User/Logout, About) - Hide create actions and admin settings on mobile - Pass menu prop to RightMenu for Dashboards link 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4 <[email protected]> --- superset-frontend/src/features/home/Menu.tsx | 28 ++++----- superset-frontend/src/features/home/RightMenu.tsx | 71 ++++++++++++++++++++++- superset-frontend/src/features/home/types.ts | 1 + 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/superset-frontend/src/features/home/Menu.tsx b/superset-frontend/src/features/home/Menu.tsx index feaabd3e4f..99fdadd543 100644 --- a/superset-frontend/src/features/home/Menu.tsx +++ b/superset-frontend/src/features/home/Menu.tsx @@ -323,7 +323,7 @@ export function Menu({ return ( <StyledHeader className="top" id="main-menu" role="navigation"> <StyledRow> - <StyledCol md={16} xs={24}> + <StyledCol md={16} xs={screens.md ? 24 : 12}> <Tooltip id="brand-tooltip" placement="bottomLeft" @@ -337,15 +337,15 @@ export function Menu({ <span>{brand.text}</span> </StyledBrandText> )} - <StyledMainNav - mode="horizontal" - data-test="navbar-top" - className="main-nav" - selectedKeys={activeTabs} - disabledOverflow - items={menu - .filter(item => screens.md || item.label === 'Dashboards') - .map(item => { + {/* Only show nav items on desktop */} + {screens.md && ( + <StyledMainNav + mode="horizontal" + data-test="navbar-top" + className="main-nav" + selectedKeys={activeTabs} + disabledOverflow + items={menu.map(item => { const props = { ...item, isFrontendRoute: isFrontendRoute(item.url), @@ -363,15 +363,17 @@ export function Menu({ return buildMenuItem(props); })} - /> + /> + )} </StyledCol> - <Col md={8} xs={24}> + <Col md={8} xs={screens.md ? 24 : 12}> <RightMenu - align={screens.md ? 'flex-end' : 'flex-start'} + align="flex-end" settings={settings} navbarRight={navbarRight} isFrontendRoute={isFrontendRoute} environmentTag={environmentTag} + menu={menu} /> </Col> </StyledRow> diff --git a/superset-frontend/src/features/home/RightMenu.tsx b/superset-frontend/src/features/home/RightMenu.tsx index 756267e2de..f772b76e27 100644 --- a/superset-frontend/src/features/home/RightMenu.tsx +++ b/superset-frontend/src/features/home/RightMenu.tsx @@ -101,6 +101,7 @@ const RightMenu = ({ navbarRight, isFrontendRoute, environmentTag, + menu, setQuery, }: RightMenuProps & { setQuery: ({ @@ -609,6 +610,64 @@ const RightMenu = ({ handleLogout, ]); + // Build mobile menu items - consumption only (no create/admin actions) + const mobileMenuItems = useMemo(() => { + const items: MenuItem[] = []; + + // Add Dashboards link at top (from main menu) + const dashboardsMenu = menu?.find( + item => item.label === 'Dashboards' || item.name === 'Dashboards', + ); + if (dashboardsMenu) { + const dashboardUrl = dashboardsMenu.url || '/dashboard/list/'; + items.push({ + key: 'dashboards', + label: isFrontendRoute(dashboardUrl) ? ( + <Link to={dashboardUrl}>{t('Dashboards')}</Link> + ) : ( + <Typography.Link href={dashboardUrl}> + {t('Dashboards')} + </Typography.Link> + ), + icon: <Icons.DashboardOutlined />, + }); + } + + // Add theme menu (flatten children directly) + menuItems.forEach(item => { + if (!item || !('key' in item)) return; + + // Only include theme-sub-menu and language picker + if (item.key === 'theme-sub-menu' || item.key === 'language-picker') { + items.push({ type: 'divider', key: `divider-before-${item.key}` }); + + if ('children' in item && item.children) { + // Theme menu already has a nested group, so just add its children directly + item.children.forEach(child => { + items.push(child); + }); + } else { + items.push(item); + } + } + + // Extract user-related items from settings + if (item.key === 'settings' && 'children' in item && item.children) { + item.children.forEach(child => { + if (!child || !('key' in child)) return; + + // Only include user-section and about-section + if (child.key === 'user-section' || child.key === 'about-section') { + items.push({ type: 'divider', key: `divider-before-${child.key}` }); + items.push(child); + } + }); + } + }); + + return items; + }, [menu, menuItems, isFrontendRoute]); + return ( <StyledDiv align={align}> {canDatabase && ( @@ -680,11 +739,15 @@ const RightMenu = ({ <Icons.MenuOutlined iconSize="l" /> </Button> <Drawer - title={t('Menu')} + title={null} placement="right" onClose={() => setMobileMenuOpen(false)} open={mobileMenuOpen} width={280} + styles={{ + header: { display: 'none' }, + body: { padding: 0 }, + }} > <Menu mode="inline" @@ -693,8 +756,10 @@ const RightMenu = ({ handleMenuSelection(info); setMobileMenuOpen(false); }} - onOpenChange={onMenuOpen} - items={menuItems} + items={mobileMenuItems} + css={css` + border-inline-end: none !important; + `} /> </Drawer> </> diff --git a/superset-frontend/src/features/home/types.ts b/superset-frontend/src/features/home/types.ts index a59e9fcd85..92b0da2826 100644 --- a/superset-frontend/src/features/home/types.ts +++ b/superset-frontend/src/features/home/types.ts @@ -45,6 +45,7 @@ export interface RightMenuProps { text: string; color: string; }; + menu?: MenuObjectProps[]; } export enum GlobalMenuDataOptions {
