This is an automated email from the ASF dual-hosted git repository.

kaxilnaik pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git

commit 6f222c1235c41c5cb1a57c37189dbe7d30f1e80a
Author: LI,JHE-CHEN <[email protected]>
AuthorDate: Wed Sep 17 10:49:49 2025 -0400

    UI fixes for calendar (#55476)
    
    * fix: modify calendar UI for non-selected year cells
    
    * style: UI design roll back
    
    * feat(ui): enhance calendar tooltip with rich content and filter support
    
    * fix(i18n): Revert new translation key to use fallback
    
    * style: use semantic values for colors
    
    * feat(ui): implement Portal-based calendar tooltips with theme support
    
    (cherry picked from commit c76e3dd336109f82b8b2baa30aea18299f4903e6)
---
 .../src/airflow/ui/src/components/HoverTooltip.tsx |  63 ++++++++++
 .../airflow/ui/src/pages/Dag/Calendar/Calendar.tsx |  10 +-
 .../ui/src/pages/Dag/Calendar/CalendarCell.tsx     |  60 ++++++----
 .../ui/src/pages/Dag/Calendar/CalendarLegend.tsx   |   2 +-
 .../ui/src/pages/Dag/Calendar/CalendarTooltip.tsx  | 132 ++++++++++++++++-----
 .../pages/Dag/Calendar/CalendarTooltipContent.tsx  |  68 -----------
 .../src/pages/Dag/Calendar/DailyCalendarView.tsx   |  25 ++--
 .../src/pages/Dag/Calendar/HourlyCalendarView.tsx  |  26 +++-
 .../ui/src/pages/Dag/Calendar/calendarUtils.ts     |  22 +---
 .../ui/src/pages/Dag/Calendar/richTooltipUtils.ts  |  50 --------
 .../ui/src/pages/Dag/Calendar/useDelayedTooltip.ts |  60 ----------
 11 files changed, 251 insertions(+), 267 deletions(-)

diff --git a/airflow-core/src/airflow/ui/src/components/HoverTooltip.tsx 
b/airflow-core/src/airflow/ui/src/components/HoverTooltip.tsx
new file mode 100644
index 00000000000..46466858cae
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/components/HoverTooltip.tsx
@@ -0,0 +1,63 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Portal } from "@chakra-ui/react";
+import { useState, useRef, useCallback, cloneElement } from "react";
+import type { ReactElement, ReactNode, RefObject } from "react";
+
+type Props = {
+  readonly children: ReactElement;
+  readonly delayMs?: number;
+  readonly tooltip: (triggerRef: RefObject<HTMLElement>) => ReactNode;
+};
+
+export const HoverTooltip = ({ children, delayMs = 200, tooltip }: Props) => {
+  const triggerRef = useRef<HTMLElement>(null);
+  const [isOpen, setIsOpen] = useState(false);
+  const timeoutRef = useRef<NodeJS.Timeout>();
+
+  const handleMouseEnter = useCallback(() => {
+    if (timeoutRef.current) {
+      clearTimeout(timeoutRef.current);
+    }
+    timeoutRef.current = setTimeout(() => {
+      setIsOpen(true);
+    }, delayMs);
+  }, [delayMs]);
+
+  const handleMouseLeave = useCallback(() => {
+    if (timeoutRef.current) {
+      clearTimeout(timeoutRef.current);
+      timeoutRef.current = undefined;
+    }
+    setIsOpen(false);
+  }, []);
+
+  const trigger = cloneElement(children, {
+    onMouseEnter: handleMouseEnter,
+    onMouseLeave: handleMouseLeave,
+    ref: triggerRef,
+  });
+
+  return (
+    <>
+      {trigger}
+      {Boolean(isOpen) && <Portal>{tooltip(triggerRef)}</Portal>}
+    </>
+  );
+};
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/Calendar.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/Calendar.tsx
index 72678265165..634ff609e35 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/Calendar.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/Calendar.tsx
@@ -237,7 +237,7 @@ export const Calendar = () => {
               <Box
                 animation={`${spin} 1s linear infinite`}
                 border="3px solid"
-                borderColor={{ _dark: "border.emphasized", _light: "brand.100" 
}}
+                borderColor={{ _dark: "none.600", _light: "brand.100" }}
                 borderRadius="50%"
                 borderTopColor="brand.500"
                 height="24px"
@@ -248,7 +248,12 @@ export const Calendar = () => {
         ) : undefined}
         {granularity === "daily" ? (
           <>
-            <DailyCalendarView data={data?.dag_runs ?? []} scale={scale} 
selectedYear={selectedDate.year()} />
+            <DailyCalendarView
+              data={data?.dag_runs ?? []}
+              scale={scale}
+              selectedYear={selectedDate.year()}
+              viewMode={viewMode}
+            />
             <CalendarLegend scale={scale} viewMode={viewMode} />
           </>
         ) : (
@@ -259,6 +264,7 @@ export const Calendar = () => {
                 scale={scale}
                 selectedMonth={selectedDate.month()}
                 selectedYear={selectedDate.year()}
+                viewMode={viewMode}
               />
             </Box>
             <Box display="flex" flex="1" justifyContent="center" pt={16}>
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarCell.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarCell.tsx
index fe06ea93776..da6973810ea 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarCell.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarCell.tsx
@@ -17,38 +17,56 @@
  * under the License.
  */
 import { Box } from "@chakra-ui/react";
+import type React from "react";
+
+import { HoverTooltip } from "src/components/HoverTooltip";
 
 import { CalendarTooltip } from "./CalendarTooltip";
-import { CalendarTooltipContent } from "./CalendarTooltipContent";
-import type { CalendarCellData } from "./types";
-import { useDelayedTooltip } from "./useDelayedTooltip";
+import type { CalendarCellData, CalendarColorMode } from "./types";
 
 type Props = {
   readonly backgroundColor: Record<string, string> | string;
-  readonly cellData?: CalendarCellData; // For rich tooltip content
+  readonly cellData: CalendarCellData | undefined;
   readonly index?: number;
   readonly marginRight?: string;
+  readonly viewMode?: CalendarColorMode;
 };
 
-export const CalendarCell = ({ backgroundColor, cellData, index, marginRight 
}: Props) => {
-  const { handleMouseEnter, handleMouseLeave } = useDelayedTooltip();
+const renderTooltip =
+  (cellData: CalendarCellData | undefined, viewMode: CalendarColorMode) =>
+  (triggerRef: React.RefObject<HTMLElement>) => (
+    <CalendarTooltip cellData={cellData} triggerRef={triggerRef} 
viewMode={viewMode} />
+  );
 
+export const CalendarCell = ({
+  backgroundColor,
+  cellData,
+  index,
+  marginRight,
+  viewMode = "total",
+}: Props) => {
   const computedMarginRight = marginRight ?? (index !== undefined && index % 7 
=== 6 ? "8px" : "0");
 
-  return (
-    <Box onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} 
position="relative">
-      <Box
-        _hover={{ transform: "scale(1.1)" }}
-        bg={backgroundColor}
-        border="1px solid"
-        borderColor="border.emphasized"
-        borderRadius="2px"
-        cursor="pointer"
-        height="14px"
-        marginRight={computedMarginRight}
-        width="14px"
-      />
-      <CalendarTooltip content={cellData ? <CalendarTooltipContent 
cellData={cellData} /> : ""} />
-    </Box>
+  const relevantCount =
+    viewMode === "failed" ? (cellData?.counts.failed ?? 0) : 
(cellData?.counts.total ?? 0);
+  const hasData = Boolean(cellData && relevantCount > 0);
+  const hasTooltip = Boolean(cellData);
+
+  const cellBox = (
+    <Box
+      _hover={hasData ? { transform: "scale(1.1)" } : {}}
+      bg={backgroundColor}
+      borderRadius="2px"
+      cursor={hasData ? "pointer" : "default"}
+      height="14px"
+      marginRight={computedMarginRight}
+      width="14px"
+    />
   );
+
+  if (!hasTooltip) {
+    return cellBox;
+  }
+
+  return <HoverTooltip tooltip={renderTooltip(cellData, 
viewMode)}>{cellBox}</HoverTooltip>;
 };
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarLegend.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarLegend.tsx
index afb963e6c96..86ed307f8d8 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarLegend.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarLegend.tsx
@@ -84,7 +84,7 @@ export const CalendarLegend = ({ scale, vertical = false, 
viewMode }: Props) =>
         <HStack gap={4} justify="center" wrap="wrap">
           <HStack gap={2}>
             <Box
-              bg={{ _dark: "scheduled.600", _light: "scheduled.200" }}
+              bg={{ _dark: "stone.600", _light: "stone.200" }}
               borderRadius="2px"
               boxShadow="sm"
               height="14px"
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarTooltip.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarTooltip.tsx
index ac8a03679a4..d4c919436bd 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarTooltip.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarTooltip.tsx
@@ -16,49 +16,58 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { Box, HStack, Text, VStack } from "@chakra-ui/react";
 import { useMemo } from "react";
-import type { ReactNode } from "react";
+import type { RefObject } from "react";
+import { useTranslation } from "react-i18next";
 
-const TOOLTIP_MIN_WIDTH = "200px";
-const TOOLTIP_MAX_WIDTH = "350px";
-const TOOLTIP_MAX_HEIGHT = "200px";
-const TOOLTIP_PADDING = "12px";
+import type { CalendarCellData, CalendarColorMode } from "./types";
+
+const SQUARE_SIZE = "12px";
+const SQUARE_BORDER_RADIUS = "2px";
 
 type Props = {
-  readonly content: ReactNode;
+  readonly cellData: CalendarCellData | undefined;
+  readonly triggerRef: RefObject<HTMLElement>;
+  readonly viewMode?: CalendarColorMode;
 };
 
-export const CalendarTooltip = ({ content }: Props) => {
-  const tooltipStyle = useMemo(
-    () => ({
-      backgroundColor: "var(--chakra-colors-bg-muted)",
-      border: "1px solid var(--chakra-colors-border-emphasized)",
+const stateColorMap = {
+  failed: "failed.solid",
+  planned: "stone.solid",
+  running: "running.solid",
+  success: "success.solid",
+};
+
+export const CalendarTooltip = ({ cellData, triggerRef, viewMode = "total" }: 
Props) => {
+  const { t: translate } = useTranslation(["dag", "common"]);
+
+  const tooltipStyle = useMemo(() => {
+    if (!triggerRef.current) {
+      return { display: "none" };
+    }
+
+    const rect = triggerRef.current.getBoundingClientRect();
+
+    return {
+      backgroundColor: "var(--chakra-colors-bg-inverted)",
       borderRadius: "4px",
-      color: "fg",
+      color: "var(--chakra-colors-fg-inverted)",
       fontSize: "14px",
-      left: "50%",
-      maxHeight: TOOLTIP_MAX_HEIGHT,
-      maxWidth: TOOLTIP_MAX_WIDTH,
-      minWidth: TOOLTIP_MIN_WIDTH,
-      opacity: 0,
-      overflowY: "auto" as const,
-      padding: TOOLTIP_PADDING,
-      pointerEvents: "none" as const,
+      left: `${rect.left + globalThis.scrollX + rect.width / 2}px`,
+      minWidth: "200px",
+      padding: "8px",
       position: "absolute" as const,
-      top: "22px",
+      top: `${rect.bottom + globalThis.scrollY + 8}px`,
       transform: "translateX(-50%)",
-      transition: "opacity 0.2s, visibility 0.2s",
-      visibility: "hidden" as const,
-      whiteSpace: "normal" as const,
-      width: "auto",
+      whiteSpace: "nowrap" as const,
       zIndex: 1000,
-    }),
-    [],
-  );
+    };
+  }, [triggerRef]);
 
   const arrowStyle = useMemo(
     () => ({
-      borderBottom: "4px solid var(--chakra-colors-bg-muted)",
+      borderBottom: "4px solid var(--chakra-colors-bg-inverted)",
       borderLeft: "4px solid transparent",
       borderRight: "4px solid transparent",
       content: '""',
@@ -72,10 +81,71 @@ export const CalendarTooltip = ({ content }: Props) => {
     [],
   );
 
+  if (!cellData) {
+    return undefined;
+  }
+
+  const { counts, date } = cellData;
+
+  const relevantCount = viewMode === "failed" ? counts.failed : counts.total;
+  const hasRuns = relevantCount > 0;
+
+  // In failed mode, only show failed runs; in total mode, show all non-zero 
states
+  const states = Object.entries(counts)
+    .filter(([key, value]) => {
+      if (key === "total") {
+        return false;
+      }
+      if (value === 0) {
+        return false;
+      }
+      if (viewMode === "failed") {
+        return key === "failed";
+      }
+
+      return true;
+    })
+    .map(([state, count]) => ({
+      color: stateColorMap[state as keyof typeof stateColorMap] || "gray.500",
+      count,
+      state: translate(`common:states.${state}`),
+    }));
+
   return (
-    <div data-tooltip style={tooltipStyle}>
+    <div style={tooltipStyle}>
       <div style={arrowStyle} />
-      {content}
+      {hasRuns ? (
+        <VStack align="start" gap={2}>
+          <Text fontSize="sm" fontWeight="medium">
+            {date}
+          </Text>
+          <VStack align="start" gap={1.5}>
+            {states.map(({ color, count, state }) => (
+              <HStack gap={3} key={state}>
+                <Box
+                  bg={color}
+                  border="1px solid"
+                  borderColor="border.emphasized"
+                  borderRadius={SQUARE_BORDER_RADIUS}
+                  height={SQUARE_SIZE}
+                  width={SQUARE_SIZE}
+                />
+                <Text fontSize="xs">
+                  {count} {state}
+                </Text>
+              </HStack>
+            ))}
+          </VStack>
+        </VStack>
+      ) : (
+        <Text fontSize="sm">
+          {/* To do: remove fallback translations */}
+          {date}:{" "}
+          {viewMode === "failed"
+            ? translate("calendar.noFailedRuns", "No failed runs")
+            : translate("calendar.noRuns", "No runs")}
+        </Text>
+      )}
     </div>
   );
 };
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarTooltipContent.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarTooltipContent.tsx
deleted file mode 100644
index ebc483c40a4..00000000000
--- 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/CalendarTooltipContent.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { Box, HStack, Text, VStack } from "@chakra-ui/react";
-import { useTranslation } from "react-i18next";
-
-import { createRichTooltipContent } from "./richTooltipUtils";
-import type { CalendarCellData } from "./types";
-
-const SQUARE_SIZE = "12px";
-const SQUARE_BORDER_RADIUS = "2px";
-
-type Props = {
-  readonly cellData: CalendarCellData;
-};
-
-export const CalendarTooltipContent = ({ cellData }: Props) => {
-  const { t: translate } = useTranslation("dag");
-  const { date, hasRuns, states, total } = createRichTooltipContent(cellData);
-
-  if (!hasRuns) {
-    return (
-      <Text fontSize="sm">
-        {date}: {translate("calendar.noRuns")}
-      </Text>
-    );
-  }
-
-  return (
-    <VStack align="start" gap={2}>
-      <Text fontSize="sm" fontWeight="medium">
-        {date}: {total} {translate("calendar.runs")}
-      </Text>
-      <VStack align="start" gap={1.5}>
-        {states.map(({ color, count, state }) => (
-          <HStack gap={3} key={state}>
-            <Box
-              bg={color}
-              border="1px solid"
-              borderColor="border.emphasized"
-              borderRadius={SQUARE_BORDER_RADIUS}
-              height={SQUARE_SIZE}
-              width={SQUARE_SIZE}
-            />
-            <Text fontSize="xs">
-              {count} {state}
-            </Text>
-          </HStack>
-        ))}
-      </VStack>
-    </VStack>
-  );
-};
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/DailyCalendarView.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/DailyCalendarView.tsx
index c67b91a4726..c34a22c8b78 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/DailyCalendarView.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/DailyCalendarView.tsx
@@ -43,15 +43,16 @@ import type { CalendarTimeRangeResponse } from 
"openapi/requests/types.gen";
 
 import { CalendarCell } from "./CalendarCell";
 import { generateDailyCalendarData } from "./calendarUtils";
-import type { CalendarScale } from "./types";
+import type { CalendarScale, CalendarColorMode } from "./types";
 
 type Props = {
   readonly data: Array<CalendarTimeRangeResponse>;
   readonly scale: CalendarScale;
   readonly selectedYear: number;
+  readonly viewMode?: CalendarColorMode;
 };
 
-export const DailyCalendarView = ({ data, scale, selectedYear }: Props) => {
+export const DailyCalendarView = ({ data, scale, selectedYear, viewMode = 
"total" }: Props) => {
   const { t: translate } = useTranslation("dag");
   const dailyData = generateDailyCalendarData(data, selectedYear);
 
@@ -107,19 +108,23 @@ export const DailyCalendarView = ({ data, scale, 
selectedYear }: Props) => {
                 const isInSelectedYear = dayDate.year() === selectedYear;
 
                 if (!isInSelectedYear) {
-                  const emptyCellData = {
-                    counts: { failed: 0, planned: 0, queued: 0, running: 0, 
success: 0, total: 0 },
-                    date: day.date,
-                    runs: [],
-                  };
-
                   return (
-                    <CalendarCell backgroundColor="transparent" 
cellData={emptyCellData} key={day.date} />
+                    <CalendarCell
+                      backgroundColor="transparent"
+                      cellData={undefined}
+                      key={day.date}
+                      viewMode={viewMode}
+                    />
                   );
                 }
 
                 return (
-                  <CalendarCell backgroundColor={scale.getColor(day.counts)} 
cellData={day} key={day.date} />
+                  <CalendarCell
+                    backgroundColor={scale.getColor(day.counts)}
+                    cellData={day}
+                    key={day.date}
+                    viewMode={viewMode}
+                  />
                 );
               })}
             </Box>
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/HourlyCalendarView.tsx 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/HourlyCalendarView.tsx
index 38bb50c50fc..0979f5efcb6 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/HourlyCalendarView.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/HourlyCalendarView.tsx
@@ -44,7 +44,7 @@ import type { CalendarTimeRangeResponse } from 
"openapi/requests/types.gen";
 
 import { CalendarCell } from "./CalendarCell";
 import { generateHourlyCalendarData } from "./calendarUtils";
-import type { CalendarScale } from "./types";
+import type { CalendarScale, CalendarColorMode } from "./types";
 
 dayjs.extend(isSameOrBefore);
 
@@ -53,9 +53,16 @@ type Props = {
   readonly scale: CalendarScale;
   readonly selectedMonth: number;
   readonly selectedYear: number;
+  readonly viewMode?: CalendarColorMode;
 };
 
-export const HourlyCalendarView = ({ data, scale, selectedMonth, selectedYear 
}: Props) => {
+export const HourlyCalendarView = ({
+  data,
+  scale,
+  selectedMonth,
+  selectedYear,
+  viewMode = "total",
+}: Props) => {
   const { t: translate } = useTranslation("dag");
   const hourlyData = generateHourlyCalendarData(data, selectedYear, 
selectedMonth);
 
@@ -159,28 +166,35 @@ export const HourlyCalendarView = ({ data, scale, 
selectedMonth, selectedYear }:
 
                 if (!hourData) {
                   const emptyCounts = { failed: 0, planned: 0, queued: 0, 
running: 0, success: 0, total: 0 };
-                  const emptyCellData = {
+                  const emptyData = {
                     counts: emptyCounts,
-                    date: `${day.day}T${hour.toString().padStart(2, 
"0")}:00:00`,
+                    date: `${dayjs(day.day).format("MMM DD")}, 
${hour.toString().padStart(2, "0")}:00`,
                     runs: [],
                   };
 
                   return (
                     <CalendarCell
                       backgroundColor={scale.getColor(emptyCounts)}
-                      cellData={emptyCellData}
+                      cellData={emptyData}
                       index={index}
                       key={`${day.day}-${hour}`}
+                      viewMode={viewMode}
                     />
                   );
                 }
 
+                const formattedHourData = {
+                  ...hourData,
+                  date: `${dayjs(day.day).format("MMM DD")}, 
${hour.toString().padStart(2, "0")}:00`,
+                };
+
                 return (
                   <CalendarCell
                     backgroundColor={scale.getColor(hourData.counts)}
-                    cellData={hourData}
+                    cellData={formattedHourData}
                     index={index}
                     key={`${day.day}-${hour}`}
+                    viewMode={viewMode}
                   />
                 );
               })}
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/calendarUtils.ts 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/calendarUtils.ts
index 7f145f41c22..4ea24f89f81 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/calendarUtils.ts
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/calendarUtils.ts
@@ -25,7 +25,6 @@ import type {
   RunCounts,
   DailyCalendarData,
   HourlyCalendarData,
-  CalendarCellData,
   CalendarColorMode,
   CalendarGranularity,
   CalendarScale,
@@ -36,7 +35,7 @@ dayjs.extend(isSameOrBefore);
 
 // Calendar color constants
 const EMPTY_COLOR = { _dark: "gray.700", _light: "gray.100" };
-const PLANNED_COLOR = { _dark: "scheduled.600", _light: "scheduled.200" };
+const PLANNED_COLOR = { _dark: "stone.600", _light: "stone.200" };
 
 const TOTAL_COLOR_INTENSITIES = [
   EMPTY_COLOR, // 0
@@ -220,7 +219,8 @@ export const createCalendarScale = (
 
   // Handle single value case
   if (minCount === maxCount) {
-    const singleColor = viewMode === "total" ? TOTAL_COLOR_INTENSITIES[2]! : 
FAILURE_COLOR_INTENSITIES[2]!;
+    const singleColor =
+      (viewMode === "total" ? TOTAL_COLOR_INTENSITIES[2] : 
FAILURE_COLOR_INTENSITIES[2]) ?? EMPTY_COLOR;
 
     return {
       getColor: (counts: RunCounts) => {
@@ -294,7 +294,7 @@ export const createCalendarScale = (
       label = `${threshold}-${nextThreshold - 1}`;
     }
 
-    const color = colorScheme[Math.min(index, colorScheme.length - 1)]!;
+    const color = colorScheme[Math.min(index, colorScheme.length - 1)] ?? 
EMPTY_COLOR;
 
     legendItems.push({
       color,
@@ -308,17 +308,3 @@ export const createCalendarScale = (
     type: "gradient",
   };
 };
-
-export const createTooltipContent = (cellData: CalendarCellData): string => {
-  const { counts, date } = cellData;
-
-  if (counts.total === 0) {
-    return `${date}: No runs`;
-  }
-
-  const parts = Object.entries(counts)
-    .filter(([key, value]) => key !== "total" && value > 0)
-    .map(([state, count]) => `${count} ${state}`);
-
-  return `${date}: ${counts.total} runs (${parts.join(", ")})`;
-};
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/richTooltipUtils.ts 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/richTooltipUtils.ts
deleted file mode 100644
index 717ee73e03c..00000000000
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/richTooltipUtils.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import dayjs from "dayjs";
-
-import type { CalendarCellData } from "./types";
-
-export const createRichTooltipContent = (cellData: CalendarCellData) => {
-  const { counts, date } = cellData;
-  const hasRuns = counts.total > 0;
-
-  if (!hasRuns) {
-    return {
-      date: dayjs(date).format("MMM DD, YYYY"),
-      hasRuns: false,
-      states: [],
-      total: 0,
-    };
-  }
-
-  const states = Object.entries(counts)
-    .filter(([key, value]) => key !== "total" && value > 0)
-    .map(([state, count]) => ({
-      color: `var(--chakra-colors-${state}-solid)`,
-      count,
-      state,
-    }));
-
-  return {
-    date: dayjs(date).format("MMM DD, YYYY"),
-    hasRuns: true,
-    states,
-    total: counts.total,
-  };
-};
diff --git 
a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/useDelayedTooltip.ts 
b/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/useDelayedTooltip.ts
deleted file mode 100644
index b2e0d17c0ea..00000000000
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Calendar/useDelayedTooltip.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-/*!
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { useRef } from "react";
-
-export const useDelayedTooltip = (delayMs: number = 200) => {
-  const debounceTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
-  const activeTooltipRef = useRef<HTMLElement | undefined>(undefined);
-
-  const handleMouseEnter = (event: React.MouseEvent<HTMLDivElement>) => {
-    if (debounceTimeoutRef.current) {
-      clearTimeout(debounceTimeoutRef.current);
-    }
-
-    const tooltipElement = event.currentTarget.querySelector("[data-tooltip]");
-
-    if (tooltipElement) {
-      activeTooltipRef.current = tooltipElement as HTMLElement;
-      debounceTimeoutRef.current = setTimeout(() => {
-        if (activeTooltipRef.current) {
-          activeTooltipRef.current.style.opacity = "1";
-          activeTooltipRef.current.style.visibility = "visible";
-        }
-      }, delayMs);
-    }
-  };
-
-  const handleMouseLeave = () => {
-    if (debounceTimeoutRef.current) {
-      clearTimeout(debounceTimeoutRef.current);
-      debounceTimeoutRef.current = undefined;
-    }
-
-    if (activeTooltipRef.current) {
-      activeTooltipRef.current.style.opacity = "0";
-      activeTooltipRef.current.style.visibility = "hidden";
-      activeTooltipRef.current = undefined;
-    }
-  };
-
-  return {
-    handleMouseEnter,
-    handleMouseLeave,
-  };
-};

Reply via email to