This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-2-test by this push:
new 1cced913570 [v3-2-test] UI: Make DAG detail page scrollable on mobile
viewports (#65899) (#66975)
1cced913570 is described below
commit 1cced913570b11a2b3d1fe9be8e245faa93269a0
Author: Dheeraj Turaga <[email protected]>
AuthorDate: Thu May 14 23:56:41 2026 -0500
[v3-2-test] UI: Make DAG detail page scrollable on mobile viewports
(#65899) (#66975)
On phones the horizontal split between the Graph/Grid/Gantt pane and
the Details pane was squeezed to ~200px each, and nested overflow:hidden
containers hid the panes' internal scrollbars. The page was effectively
unusable on mobile.
Pin a 1280px min-width on the DAG page wrapper below Chakra's
breakpoint and switch the main-content box to overflow=auto so the
browser exposes horizontal and vertical scrollbars. Desktop layout
(>=768px) is unchanged.
(cherry picked from commit e6f5de4f5eda87c702a5a1c7625ba395221cce48)
---
.../src/airflow/ui/src/layouts/BaseLayout.tsx | 2 +-
.../ui/src/layouts/Details/DetailsLayout.tsx | 347 +++++++++++----------
2 files changed, 178 insertions(+), 171 deletions(-)
diff --git a/airflow-core/src/airflow/ui/src/layouts/BaseLayout.tsx
b/airflow-core/src/airflow/ui/src/layouts/BaseLayout.tsx
index 30023505e62..062caca76d3 100644
--- a/airflow-core/src/airflow/ui/src/layouts/BaseLayout.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/BaseLayout.tsx
@@ -113,7 +113,7 @@ export const BaseLayout = ({ children }: PropsWithChildren)
=> {
flex={1}
flexDirection="column"
minH={0}
- overflowY="auto"
+ overflow="auto"
p={3}
>
{baseReactPlugins.map((plugin) => (
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
b/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
index dacd6171f5d..89f9507cdb5 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/DetailsLayout.tsx
@@ -110,191 +110,198 @@ export const DetailsLayout = ({ children, error,
isLoading, tabs }: Props) => {
return (
<HoverProvider>
<OpenGroupsProvider dagId={dagId}>
- <HStack justifyContent="space-between" mb={2}>
- <DagBreadcrumb />
- <Flex gap={1}>
- <SearchDagsButton />
- {dag === undefined ? undefined : (
- <TriggerDAGButton
- allowedRunTypes={dag.allowed_run_types}
- dagDisplayName={dag.dag_display_name}
- dagId={dag.dag_id}
- isPaused={dag.is_paused}
- variant="outline"
- withText
- />
- )}
- </Flex>
- </HStack>
- <Toaster />
- <BackfillBanner dagId={dagId} />
- <Box flex={1} minH={0}>
- {isRightPanelCollapsed ? (
- <Tooltip content={translate("common:showDetailsPanel")}>
- <IconButton
- aria-label={translate("common:showDetailsPanel")}
- bg="fg.subtle"
- borderRadius={direction === "ltr" ? "100% 0 0 100%" : "0 100%
100% 0"}
- boxShadow="md"
- left={direction === "rtl" ? "-5px" : undefined}
- onClick={() => setIsRightPanelCollapsed(false)}
- position="absolute"
- right={direction === "ltr" ? "-5px" : undefined}
- size="2xs"
- top="50%"
- zIndex={10}
- >
- {direction === "ltr" ? <FaChevronLeft /> : <FaChevronRight />}
- </IconButton>
- </Tooltip>
- ) : undefined}
- <PanelGroup
- autoSaveId={`${dagView}-${direction}`}
- dir={direction}
- direction="horizontal"
- key={`${dagView}-${direction}`}
- ref={panelGroupRef}
- >
- <Panel
- defaultSize={dagView === "graph" ? 70 : 20}
- id="main-panel"
- minSize={showGantt && dagView === "grid" && Boolean(runId) ? 35
: 6}
- order={1}
- >
- <Box height="100%" position="relative">
- <PanelButtons
- dagRunStateFilter={dagRunStateFilter}
- dagView={dagView}
- limit={limit}
- panelGroupRef={panelGroupRef}
- runAfterGte={runAfterGte}
- runAfterLte={runAfterLte}
- runTypeFilter={runTypeFilter}
- setDagRunStateFilter={setDagRunStateFilter}
- setDagView={setDagView}
- setLimit={setLimit}
- setRunAfterGte={setRunAfterGte}
- setRunAfterLte={setRunAfterLte}
- setRunTypeFilter={setRunTypeFilter}
- setShowGantt={setShowGantt}
- setShowVersionIndicatorMode={setShowVersionIndicatorMode}
- setTriggeringUserFilter={setTriggeringUserFilter}
- showGantt={showGantt}
- showVersionIndicatorMode={showVersionIndicatorMode}
- triggeringUserFilter={triggeringUserFilter}
+ <Box display="flex" flex={1} flexDirection="column" minH={0} minW={{
base: "1280px", md: "auto" }}>
+ <HStack justifyContent="space-between" mb={2}>
+ <DagBreadcrumb />
+ <Flex gap={1}>
+ <SearchDagsButton />
+ {dag === undefined ? undefined : (
+ <TriggerDAGButton
+ allowedRunTypes={dag.allowed_run_types}
+ dagDisplayName={dag.dag_display_name}
+ dagId={dag.dag_id}
+ isPaused={dag.is_paused}
+ variant="outline"
+ withText
/>
- {dagView === "graph" ? (
- <Graph />
- ) : (
- <HStack alignItems="flex-start" gap={0}>
- <Grid
- dagRunState={dagRunStateFilter}
- limit={limit}
- runAfterGte={runAfterGte}
- runAfterLte={runAfterLte}
- runType={runTypeFilter}
- showGantt={Boolean(runId) && showGantt}
- showVersionIndicatorMode={showVersionIndicatorMode}
- triggeringUser={triggeringUserFilter}
- />
- {showGantt ? (
- <Gantt
+ )}
+ </Flex>
+ </HStack>
+ <Toaster />
+ <BackfillBanner dagId={dagId} />
+ <Box flex={1} minH={0}>
+ {isRightPanelCollapsed ? (
+ <Tooltip content={translate("common:showDetailsPanel")}>
+ <IconButton
+ aria-label={translate("common:showDetailsPanel")}
+ bg="fg.subtle"
+ borderRadius={direction === "ltr" ? "100% 0 0 100%" : "0
100% 100% 0"}
+ boxShadow="md"
+ left={direction === "rtl" ? "-5px" : undefined}
+ onClick={() => setIsRightPanelCollapsed(false)}
+ position="absolute"
+ right={direction === "ltr" ? "-5px" : undefined}
+ size="2xs"
+ top="50%"
+ zIndex={10}
+ >
+ {direction === "ltr" ? <FaChevronLeft /> : <FaChevronRight
/>}
+ </IconButton>
+ </Tooltip>
+ ) : undefined}
+ <PanelGroup
+ autoSaveId={`${dagView}-${direction}`}
+ dir={direction}
+ direction="horizontal"
+ key={`${dagView}-${direction}`}
+ ref={panelGroupRef}
+ >
+ <Panel
+ defaultSize={dagView === "graph" ? 70 : 20}
+ id="main-panel"
+ minSize={showGantt && dagView === "grid" && Boolean(runId) ?
35 : 6}
+ order={1}
+ >
+ <Box height="100%" position="relative">
+ <PanelButtons
+ dagRunStateFilter={dagRunStateFilter}
+ dagView={dagView}
+ limit={limit}
+ panelGroupRef={panelGroupRef}
+ runAfterGte={runAfterGte}
+ runAfterLte={runAfterLte}
+ runTypeFilter={runTypeFilter}
+ setDagRunStateFilter={setDagRunStateFilter}
+ setDagView={setDagView}
+ setLimit={setLimit}
+ setRunAfterGte={setRunAfterGte}
+ setRunAfterLte={setRunAfterLte}
+ setRunTypeFilter={setRunTypeFilter}
+ setShowGantt={setShowGantt}
+ setShowVersionIndicatorMode={setShowVersionIndicatorMode}
+ setTriggeringUserFilter={setTriggeringUserFilter}
+ showGantt={showGantt}
+ showVersionIndicatorMode={showVersionIndicatorMode}
+ triggeringUserFilter={triggeringUserFilter}
+ />
+ {dagView === "graph" ? (
+ <Graph />
+ ) : (
+ <HStack alignItems="flex-start" gap={0}>
+ <Grid
dagRunState={dagRunStateFilter}
limit={limit}
runAfterGte={runAfterGte}
runAfterLte={runAfterLte}
runType={runTypeFilter}
+ showGantt={Boolean(runId) && showGantt}
+ showVersionIndicatorMode={showVersionIndicatorMode}
triggeringUser={triggeringUserFilter}
/>
- ) : undefined}
- </HStack>
- )}
- </Box>
- </Panel>
- {!isRightPanelCollapsed && (
- <>
- <PanelResizeHandle
- className="resize-handle"
- onDragging={(isDragging) => {
- if (!isDragging) {
- const zoom = getZoom();
+ {showGantt ? (
+ <Gantt
+ dagRunState={dagRunStateFilter}
+ limit={limit}
+ runAfterGte={runAfterGte}
+ runAfterLte={runAfterLte}
+ runType={runTypeFilter}
+ triggeringUser={triggeringUserFilter}
+ />
+ ) : undefined}
+ </HStack>
+ )}
+ </Box>
+ </Panel>
+ {!isRightPanelCollapsed && (
+ <>
+ <PanelResizeHandle
+ className="resize-handle"
+ onDragging={(isDragging) => {
+ if (!isDragging) {
+ const zoom = getZoom();
- void fitView({ maxZoom: zoom, minZoom: zoom });
- }
- }}
- >
- <Box
- alignItems="center"
- bg="border.emphasized"
- cursor="col-resize"
- display="flex"
- h="100%"
- justifyContent="center"
- position="relative"
- w={0.5}
- // onClick={(e) => console.log(e)}
- />
- </PanelResizeHandle>
+ void fitView({ maxZoom: zoom, minZoom: zoom });
+ }
+ }}
+ >
+ <Box
+ alignItems="center"
+ bg="border.emphasized"
+ cursor="col-resize"
+ display="flex"
+ h="100%"
+ justifyContent="center"
+ position="relative"
+ w={0.5}
+ // onClick={(e) => console.log(e)}
+ />
+ </PanelResizeHandle>
- {/* Collapse button positioned next to the resize handle */}
+ {/* Collapse button positioned next to the resize handle */}
- <Panel defaultSize={dagView === "graph" ? 30 : 80}
id="details-panel" minSize={20} order={2}>
- <Box display="flex" flexDirection="column" h="100%"
position="relative">
- <Tooltip
content={translate("common:collapseDetailsPanel")}>
- <IconButton
- aria-label={translate("common:collapseDetailsPanel")}
- bg="fg.subtle"
- borderRadius={direction === "ltr" ? "0 100% 100% 0" :
"100% 0 0 100%"}
- boxShadow="md"
- left={direction === "ltr" ? "-5px" : undefined}
- onClick={() => setIsRightPanelCollapsed(true)}
- position="absolute"
- right={direction === "rtl" ? "-5px" : undefined}
- size="2xs"
- top="50%"
- zIndex={2}
- >
- {direction === "ltr" ? <FaChevronRight /> :
<FaChevronLeft />}
- </IconButton>
- </Tooltip>
- {children}
- {Boolean(error) || (warningData?.dag_warnings.length ?? 0)
> 0 ? (
- <>
- <Tooltip
- content={`${translate("common:dagWarnings")}
(${warningData?.total_entries ?? 0 + Number(error)})`}
+ <Panel
+ defaultSize={dagView === "graph" ? 30 : 80}
+ id="details-panel"
+ minSize={20}
+ order={2}
+ >
+ <Box display="flex" flexDirection="column" h="100%"
position="relative">
+ <Tooltip
content={translate("common:collapseDetailsPanel")}>
+ <IconButton
+ aria-label={translate("common:collapseDetailsPanel")}
+ bg="fg.subtle"
+ borderRadius={direction === "ltr" ? "0 100% 100% 0"
: "100% 0 0 100%"}
+ boxShadow="md"
+ left={direction === "ltr" ? "-5px" : undefined}
+ onClick={() => setIsRightPanelCollapsed(true)}
+ position="absolute"
+ right={direction === "rtl" ? "-5px" : undefined}
+ size="2xs"
+ top="50%"
+ zIndex={2}
>
- <IconButton
- aria-label={`${translate("common:dagWarnings")}
(${warningData?.total_entries ?? 0 + Number(error)})`}
- colorPalette={Boolean(error) ? "red" : "orange"}
- margin="2"
- marginBottom="-1"
- onClick={onOpen}
- rounded="full"
- size="md"
- variant="solid"
+ {direction === "ltr" ? <FaChevronRight /> :
<FaChevronLeft />}
+ </IconButton>
+ </Tooltip>
+ {children}
+ {Boolean(error) || (warningData?.dag_warnings.length ??
0) > 0 ? (
+ <>
+ <Tooltip
+ content={`${translate("common:dagWarnings")}
(${warningData?.total_entries ?? 0 + Number(error)})`}
>
- <LuFileWarning />
- </IconButton>
- </Tooltip>
+ <IconButton
+ aria-label={`${translate("common:dagWarnings")}
(${warningData?.total_entries ?? 0 + Number(error)})`}
+ colorPalette={Boolean(error) ? "red" : "orange"}
+ margin="2"
+ marginBottom="-1"
+ onClick={onOpen}
+ rounded="full"
+ size="md"
+ variant="solid"
+ >
+ <LuFileWarning />
+ </IconButton>
+ </Tooltip>
- <DAGWarningsModal
- error={error}
- onClose={onClose}
- open={open}
- warnings={warningData?.dag_warnings}
- />
- </>
- ) : undefined}
- <ProgressBar size="xs" visibility={isLoading ? "visible" :
"hidden"} />
- <NavTabs tabs={tabs} />
- <Box flexGrow={1} overflow="auto" px={2}>
- <Outlet />
+ <DAGWarningsModal
+ error={error}
+ onClose={onClose}
+ open={open}
+ warnings={warningData?.dag_warnings}
+ />
+ </>
+ ) : undefined}
+ <ProgressBar size="xs" visibility={isLoading ? "visible"
: "hidden"} />
+ <NavTabs tabs={tabs} />
+ <Box flexGrow={1} overflow="auto" px={2}>
+ <Outlet />
+ </Box>
</Box>
- </Box>
- </Panel>
- </>
- )}
- </PanelGroup>
+ </Panel>
+ </>
+ )}
+ </PanelGroup>
+ </Box>
</Box>
</OpenGroupsProvider>
</HoverProvider>