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 284e3bdd9e73e6a7071bb55624fcc7145f61ca8b Author: Evan Rusackas <[email protected]> AuthorDate: Fri Jan 9 12:24:20 2026 -0800 feat(mobile): clean up dashboard header for mobile - Hide star icon on mobile (moved to overflow menu) - Add favorite toggle, status, owner, modified info to overflow menu - Hide Edit dashboard button on mobile - Hide Enter fullscreen and Manage email report menu items on mobile - Center logo on mobile with 3-column layout for future left icon 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --- .../src/dashboard/components/Header/index.jsx | 19 ++++-- .../src/dashboard/components/Header/types.ts | 4 ++ .../Header/useHeaderActionsDropdownMenu.tsx | 68 ++++++++++++++++++++-- superset-frontend/src/features/home/Menu.tsx | 15 ++++- 4 files changed, 95 insertions(+), 11 deletions(-) diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx index 16bec548f9..9ee450bfe6 100644 --- a/superset-frontend/src/dashboard/components/Header/index.jsx +++ b/superset-frontend/src/dashboard/components/Header/index.jsx @@ -40,6 +40,7 @@ import { Tooltip, DeleteModal, UnsavedChangesModal, + Grid, } from '@superset-ui/core/components'; import { findPermission } from 'src/utils/findPermission'; import { safeStringify } from 'src/utils/safeStringify'; @@ -163,8 +164,12 @@ const discardChanges = () => { window.location.assign(url); }; +const { useBreakpoint } = Grid; + const Header = () => { const dispatch = useDispatch(); + const screens = useBreakpoint(); + const isMobile = !screens.md; const [didNotifyMaxUndoHistoryToast, setDidNotifyMaxUndoHistoryToast] = useState(false); const [emphasizeUndo, setEmphasizeUndo] = useState(false); @@ -607,7 +612,7 @@ const Header = () => { const titlePanelAdditionalItems = useMemo( () => [ - !editMode && ( + !editMode && !isMobile && ( <PublishedStatus dashboardId={dashboardInfo.id} isPublished={isPublished} @@ -617,12 +622,13 @@ const Header = () => { visible={!editMode} /> ), - !editMode && !isEmbedded && metadataBar, + !editMode && !isEmbedded && !isMobile && metadataBar, ], [ boundActionCreators.savePublished, dashboardInfo.id, editMode, + isMobile, metadataBar, isEmbedded, isPublished, @@ -715,7 +721,7 @@ const Header = () => { ) : ( <div css={actionButtonsStyle}> {NavExtension && <NavExtension />} - {userCanEdit && ( + {userCanEdit && !isMobile && ( <Button buttonStyle="secondary" onClick={handleEnterEditMode} @@ -743,6 +749,7 @@ const Header = () => { handleCtrlZ, handleEnterEditMode, hasUnsavedChanges, + isMobile, overwriteDashboard, redoLength, toggleEditMode, @@ -780,6 +787,10 @@ const Header = () => { userCanSave: userCanSaveAs, userCanCurate, isLoading, + isMobile, + isStarred, + isPublished, + saveFaveStar: boundActionCreators.saveFaveStar, showReportModal, showPropertiesModal, showRefreshModal, @@ -806,7 +817,7 @@ const Header = () => { onOpenChange: setIsDropdownVisible, }} additionalActionsMenu={menu} - showFaveStar={user?.userId && dashboardInfo?.id} + showFaveStar={user?.userId && dashboardInfo?.id && !isMobile} showTitlePanelItems /> {showingPropertiesModal && ( diff --git a/superset-frontend/src/dashboard/components/Header/types.ts b/superset-frontend/src/dashboard/components/Header/types.ts index 564635f605..cde8bc6676 100644 --- a/superset-frontend/src/dashboard/components/Header/types.ts +++ b/superset-frontend/src/dashboard/components/Header/types.ts @@ -48,6 +48,10 @@ export interface HeaderDropdownProps { forceRefreshAllCharts: () => void; hasUnsavedChanges: boolean; isLoading: boolean; + isMobile?: boolean; + isStarred?: boolean; + isPublished?: boolean; + saveFaveStar?: (id: number, isStarred: boolean) => void; layout: Layout; onSave: () => void; refreshFrequency: number; diff --git a/superset-frontend/src/dashboard/components/Header/useHeaderActionsDropdownMenu.tsx b/superset-frontend/src/dashboard/components/Header/useHeaderActionsDropdownMenu.tsx index 99d7a0c0a8..d4754614e5 100644 --- a/superset-frontend/src/dashboard/components/Header/useHeaderActionsDropdownMenu.tsx +++ b/superset-frontend/src/dashboard/components/Header/useHeaderActionsDropdownMenu.tsx @@ -35,6 +35,7 @@ import { getActiveFilters } from 'src/dashboard/util/activeDashboardFilters'; import { getUrlParam } from 'src/utils/urlUtils'; import { MenuKeys, RootState } from 'src/dashboard/types'; import { HeaderDropdownProps } from 'src/dashboard/components/Header/types'; +import getOwnerName from 'src/utils/getOwnerName'; export const useHeaderActionsMenu = ({ customCss, @@ -53,6 +54,10 @@ export const useHeaderActionsMenu = ({ userCanSave, userCanCurate, isLoading, + isMobile, + isStarred, + isPublished, + saveFaveStar, lastModifiedTime, addSuccessToast, addDangerToast, @@ -105,6 +110,11 @@ export const useHeaderActionsMenu = ({ case MenuKeys.ManageEmbedded: manageEmbedded(); break; + case 'toggle-favorite': + if (saveFaveStar) { + saveFaveStar(dashboardId, isStarred); + } + break; default: break; } @@ -116,6 +126,10 @@ export const useHeaderActionsMenu = ({ showPropertiesModal, showRefreshModal, manageEmbedded, + saveFaveStar, + dashboardId, + isStarred, + history, ], ); @@ -183,6 +197,46 @@ export const useHeaderActionsMenu = ({ const menuItems: MenuItem[] = []; + // Mobile-only: show dashboard info items in menu + if (isMobile && !editMode) { + // Favorite toggle + if (saveFaveStar) { + menuItems.push({ + key: 'toggle-favorite', + label: isStarred ? t('Remove from favorites') : t('Add to favorites'), + }); + } + + // Published status + menuItems.push({ + key: 'status-info', + label: isPublished ? t('Status: Published') : t('Status: Draft'), + disabled: true, + }); + + // Owner info + const ownerNames = + dashboardInfo?.owners?.length > 0 + ? dashboardInfo.owners.map(getOwnerName).join(', ') + : t('None'); + menuItems.push({ + key: 'owner-info', + label: `${t('Owner')}: ${ownerNames}`, + disabled: true, + }); + + // Last modified + const modifiedBy = + getOwnerName(dashboardInfo?.changed_by) || t('Not available'); + menuItems.push({ + key: 'modified-info', + label: `${t('Modified')} ${dashboardInfo?.changed_on_delta_humanized || ''} ${t('by')} ${modifiedBy}`, + disabled: true, + }); + + menuItems.push({ type: 'divider' }); + } + // Refresh dashboard if (!editMode) { menuItems.push({ @@ -199,8 +253,8 @@ export const useHeaderActionsMenu = ({ }); } - // Toggle fullscreen - if (!editMode && !isEmbedded) { + // Toggle fullscreen (hide on mobile) + if (!editMode && !isEmbedded && !isMobile) { menuItems.push({ key: MenuKeys.ToggleFullscreen, label: getUrlParam(URL_PARAMS.standalone) @@ -268,15 +322,15 @@ export const useHeaderActionsMenu = ({ // Only add divider if there are items after it const hasItemsAfterDivider = - (!editMode && reportMenuItem) || + (!editMode && reportMenuItem && !isMobile) || (editMode && !isEmpty(dashboardInfo?.metadata?.filter_scopes)); if (hasItemsAfterDivider) { menuItems.push({ type: 'divider' }); } - // Report dropdown - if (!editMode && reportMenuItem) { + // Report dropdown (hide on mobile) + if (!editMode && reportMenuItem && !isMobile) { menuItems.push(reportMenuItem); } @@ -314,11 +368,15 @@ export const useHeaderActionsMenu = ({ expandedSlices, handleMenuClick, isLoading, + isMobile, + isPublished, + isStarred, lastModifiedTime, layout, onSave, refreshFrequency, reportMenuItem, + saveFaveStar, shareMenuItems, shouldPersistRefreshFrequency, userCanCurate, diff --git a/superset-frontend/src/features/home/Menu.tsx b/superset-frontend/src/features/home/Menu.tsx index 99fdadd543..cec2879375 100644 --- a/superset-frontend/src/features/home/Menu.tsx +++ b/superset-frontend/src/features/home/Menu.tsx @@ -323,7 +323,18 @@ export function Menu({ return ( <StyledHeader className="top" id="main-menu" role="navigation"> <StyledRow> - <StyledCol md={16} xs={screens.md ? 24 : 12}> + {/* Mobile: left placeholder for future icon */} + {!screens.md && <Col xs={4} />} + <StyledCol + md={16} + xs={screens.md ? 24 : 16} + css={ + !screens.md && + css` + justify-content: center; + ` + } + > <Tooltip id="brand-tooltip" placement="bottomLeft" @@ -366,7 +377,7 @@ export function Menu({ /> )} </StyledCol> - <Col md={8} xs={screens.md ? 24 : 12}> + <Col md={8} xs={screens.md ? 24 : 4}> <RightMenu align="flex-end" settings={settings}
