This is an automated email from the ASF dual-hosted git repository.
bbovenzi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 2846242adad Show states of task groups as stacked cards in Grid view
(#75442)
2846242adad is described below
commit 2846242adad612b53c71ea304997e863ace61ce1
Author: Zhen-Lun (Kevin) Hong <[email protected]>
AuthorDate: Fri Oct 23 22:54:49 2026 +0800
Show states of task groups as stacked cards in Grid view (#75442)
* Show task groups as stacked cards in the Grid view
* Test failed task groups in the Grid stacked card
---
.../ui/src/layouts/Details/Grid/GridTI.test.tsx | 25 ++++++++++++++++++++++
.../airflow/ui/src/layouts/Details/Grid/GridTI.tsx | 9 ++++----
2 files changed, 30 insertions(+), 4 deletions(-)
diff --git
a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.test.tsx
b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.test.tsx
index ad9a51bcd96..0c6d46f92c8 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.test.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.test.tsx
@@ -53,6 +53,7 @@ const buildStackShadow = (backCardState: string) =>
type RenderGridTIOptions = {
readonly hasNote?: boolean;
readonly instance?: LightGridTaskInstanceSummary;
+ readonly isGroup?: boolean;
readonly isMapped?: boolean;
readonly runId?: string;
readonly taskId?: string;
@@ -63,6 +64,7 @@ const renderGridTI = (
{
hasNote,
instance = taskInstance,
+ isGroup,
isMapped,
runId = SELECTED_RUN_ID,
taskId = "selected_task",
@@ -79,6 +81,7 @@ const renderGridTI = (
dagId="example_dag"
hasNote={hasNote}
instance={{ ...instance, task_id: taskId }}
+ isGroup={isGroup}
isMapped={isMapped}
label={taskId}
runId={runId}
@@ -146,6 +149,28 @@ describe("GridTI", () => {
},
);
+ it.each<{
+ backCardState: string;
+ childStates: Record<string, number>;
+ primaryState: LightGridTaskInstanceSummary["state"];
+ }>([
+ { backCardState: "skipped", childStates: { skipped: 2, success: 1 },
primaryState: "success" },
+ { backCardState: "success", childStates: { success: 3 }, primaryState:
"success" },
+ {
+ backCardState: "upstream_failed",
+ childStates: { failed: 1, success: 1, upstream_failed: 2 },
+ primaryState: "failed",
+ },
+ { backCardState: "success", childStates: { failed: 2, success: 3 },
primaryState: "failed" },
+ ])("stacks task group cells like mapped tasks", ({ backCardState,
childStates, primaryState }) => {
+
renderGridTI(`/dags/example_dag/runs/${SELECTED_RUN_ID}/tasks/selected_task`, {
+ instance: { ...taskInstance, child_states: childStates, state:
primaryState },
+ isGroup: true,
+ });
+
+
expect(screen.getByTestId("task-state-badge").style.boxShadow).toBe(buildStackShadow(backCardState));
+ });
+
it("shows a single card for unmapped tasks", () => {
renderGridTI(`/dags/example_dag/runs/${SELECTED_RUN_ID}/tasks/selected_task`, {
instance: { ...taskInstance, child_states: { skipped: 2, success: 1 } },
diff --git a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx
b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx
index 5bc78375e0e..fbfb1860c6a 100644
--- a/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx
+++ b/airflow-core/src/airflow/ui/src/layouts/Details/Grid/GridTI.tsx
@@ -87,13 +87,14 @@ export const GridTI = ({
const isSelectedRow = selectedTaskId === taskId || selectedGroupId ===
taskId;
const isSelectedTaskInstance = selectedRunId === runId && isSelectedRow;
const selectedOutlineColor = SELECTED_TASK_OUTLINE_COLOR[colorMode];
+ const isStacked = Boolean(isMapped) || Boolean(isGroup);
const frontCardState = instance.state ?? "none";
- const backCardState = isMapped
+ const backCardState = isStacked
? (sortStateEntries(instance.child_states).find(([state]) => state !==
frontCardState)?.[0] ??
frontCardState)
: undefined;
- const badgeSize = `${isMapped ? TASK_BAR_HEIGHT_PX - 1 :
TASK_BAR_HEIGHT_PX}px`;
- const stackPadding = isMapped ? `${BACK_CARD_OFFSET_PX}px` : undefined;
+ const badgeSize = `${isStacked ? TASK_BAR_HEIGHT_PX - 1 :
TASK_BAR_HEIGHT_PX}px`;
+ const stackPadding = isStacked ? `${BACK_CARD_OFFSET_PX}px` : undefined;
return (
<Flex
@@ -123,7 +124,7 @@ export const GridTI = ({
borderRadius={4}
display="flex"
outlineColor={isSelectedTaskInstance ? selectedOutlineColor :
undefined}
- outlineOffset={isMapped ? "0px" : "1px"}
+ outlineOffset={isStacked ? "0px" : "1px"}
outlineStyle={isSelectedTaskInstance ? "solid" : undefined}
outlineWidth={isSelectedTaskInstance ? "2px" : undefined}
pb={stackPadding}