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 fc7204297f9  Fix paused filter not showing all dags (#62269)
fc7204297f9 is described below

commit fc7204297f992982fd6cfafa7e411e8c812d4b17
Author: Guan-Ming (Wesley) Chiu <[email protected]>
AuthorDate: Tue Feb 24 06:46:25 2026 +0800

     Fix paused filter not showing all dags (#62269)
    
    Signed-off-by: Guan-Ming (Wesley) Chiu 
<[email protected]>
---
 .../src/airflow/ui/src/mocks/handlers/dags.ts      | 231 ++++++++++-----------
 .../DagsList/DagsFilters/DagsFilters.test.tsx      |  73 +++++++
 .../src/pages/DagsList/DagsFilters/DagsFilters.tsx |   2 +-
 3 files changed, 181 insertions(+), 125 deletions(-)

diff --git a/airflow-core/src/airflow/ui/src/mocks/handlers/dags.ts 
b/airflow-core/src/airflow/ui/src/mocks/handlers/dags.ts
index 9b9a6a205f3..2c97bef0c8b 100644
--- a/airflow-core/src/airflow/ui/src/mocks/handlers/dags.ts
+++ b/airflow-core/src/airflow/ui/src/mocks/handlers/dags.ts
@@ -16,76 +16,112 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-/* eslint-disable unicorn/no-null */
 import { http, HttpResponse, type HttpHandler } from "msw";
 
+const successDag = {
+  dag_display_name: "tutorial_taskflow_api_success",
+  dag_id: "tutorial_taskflow_api_success",
+  file_token:
+    
".eJw9yUsOgCAMBcC7cAB7JPISizR82kCJcnvjxtUsJlDWxlQwPEvhjU7TV0pk27N2goxU9f7lB80qxxPXJF-uQ1CjY5avI0wO2-EFouohiw.fhdU5u0Pb7lElEd-AUUXqjHSsdo",
+  fileloc: "/airflow/dags/tutorial_taskflow_api.py",
+  has_import_errors: false,
+  has_task_concurrency_limits: false,
+  is_favorite: true,
+  is_paused: false,
+  is_stale: false,
+  last_parsed_time: "2025-01-13T07:34:01.593459Z",
+  latest_dag_runs: [
+    {
+      dag_id: "tutorial_taskflow_api",
+      end_date: "2025-01-13T04:34:12.143831Z",
+      id: 1,
+      logical_date: "2025-01-13T04:33:58.396323Z",
+      run_id: "manual__2025-01-13T04:33:58.387988+00:00",
+      start_date: "2025-01-13T04:33:58.496197Z",
+      state: "success",
+    },
+  ],
+  max_active_runs: 16,
+  max_active_tasks: 16,
+  max_consecutive_failed_dag_runs: 0,
+  owners: ["airflow"],
+  pending_actions: [],
+  tags: [{ dag_id: "tutorial_taskflow_api_success", name: "example" }],
+  timetable_description: "Never, external triggers only",
+};
+
+const failedDag = {
+  dag_display_name: "tutorial_taskflow_api_failed",
+  dag_id: "tutorial_taskflow_api_failed",
+  file_token:
+    
".eJw9yUsOgCAMBcC7cAB7JPISizR82kCJcnvjxtUsJlDWxlQwPEvhjU7TV0pk27N2goxU9f7lB80qxxPXJF-uQ1CjY5avI0wO2-EFouohiw.fhdU5u0Pb7lElEd-AUUXqjHSsdo",
+  fileloc: "/airflow/dags/tutorial_taskflow_api_failed.py",
+  has_import_errors: false,
+  has_task_concurrency_limits: false,
+  is_favorite: false,
+  is_paused: false,
+  is_stale: false,
+  last_parsed_time: "2025-01-13T07:34:01.593459Z",
+  latest_dag_runs: [
+    {
+      dag_id: "tutorial_taskflow_api",
+      end_date: "2025-01-13T04:34:12.143831Z",
+      id: 2,
+      logical_date: "2025-01-13T04:33:58.396323Z",
+      run_id: "manual__2025-01-13T04:33:58.387988+00:00",
+      start_date: "2025-01-13T04:33:58.496197Z",
+      state: "success",
+    },
+  ],
+  max_active_runs: 16,
+  max_active_tasks: 16,
+  max_consecutive_failed_dag_runs: 0,
+  owners: ["airflow"],
+  pending_actions: [],
+  tags: [{ dag_id: "tutorial_taskflow_api_failed", name: "example" }],
+  timetable_description: "Never, external triggers only",
+};
+
+const pausedDag = {
+  dag_display_name: "paused_dag",
+  dag_id: "paused_dag",
+  file_token:
+    
".eJw9yUsOgCAMBcC7cAB7JPISizR82kCJcnvjxtUsJlDWxlQwPEvhjU7TV0pk27N2goxU9f7lB80qxxPXJF-uQ1CjY5avI0wO2-EFouohiw.fhdU5u0Pb7lElEd-AUUXqjHSsdo",
+  fileloc: "/airflow/dags/paused_dag.py",
+  has_import_errors: false,
+  has_task_concurrency_limits: false,
+  is_favorite: false,
+  is_paused: true,
+  is_stale: false,
+  last_parsed_time: "2025-01-13T07:34:01.593459Z",
+  latest_dag_runs: [],
+  max_active_runs: 16,
+  max_active_tasks: 16,
+  max_consecutive_failed_dag_runs: 0,
+  owners: ["airflow"],
+  pending_actions: [],
+  tags: [{ dag_id: "paused_dag", name: "example" }],
+  timetable_description: "Never, external triggers only",
+};
+
+const filterDagsByPaused = (paused: string | null) => {
+  const allDags = [successDag, failedDag, pausedDag];
+
+  if (paused === "true") {
+    return allDags.filter((dag) => dag.is_paused);
+  }
+  if (paused === "false") {
+    return allDags.filter((dag) => !dag.is_paused);
+  }
+
+  return allDags;
+};
+
 export const handlers: Array<HttpHandler> = [
   http.get("/ui/dags", ({ request }) => {
     const url = new URL(request.url);
     const lastDagRunState = url.searchParams.get("last_dag_run_state");
-    const successDag = {
-      dag_display_name: "tutorial_taskflow_api_success",
-      dag_id: "tutorial_taskflow_api_success",
-      file_token:
-        
".eJw9yUsOgCAMBcC7cAB7JPISizR82kCJcnvjxtUsJlDWxlQwPEvhjU7TV0pk27N2goxU9f7lB80qxxPXJF-uQ1CjY5avI0wO2-EFouohiw.fhdU5u0Pb7lElEd-AUUXqjHSsdo",
-      fileloc: "/airflow/dags/tutorial_taskflow_api.py",
-      has_import_errors: false,
-      has_task_concurrency_limits: false,
-      is_favorite: true,
-      is_paused: false,
-      is_stale: false,
-      last_parsed_time: "2025-01-13T07:34:01.593459Z",
-      latest_dag_runs: [
-        {
-          dag_id: "tutorial_taskflow_api",
-          end_date: "2025-01-13T04:34:12.143831Z",
-          id: 1,
-          logical_date: "2025-01-13T04:33:58.396323Z",
-          run_id: "manual__2025-01-13T04:33:58.387988+00:00",
-          start_date: "2025-01-13T04:33:58.496197Z",
-          state: "success",
-        },
-      ],
-      max_active_runs: 16,
-      max_active_tasks: 16,
-      max_consecutive_failed_dag_runs: 0,
-      owners: ["airflow"],
-      pending_actions: [],
-      tags: [{ dag_id: "tutorial_taskflow_api_success", name: "example" }],
-      timetable_description: "Never, external triggers only",
-    };
-    const failedDag = {
-      dag_display_name: "tutorial_taskflow_api_failed",
-      dag_id: "tutorial_taskflow_api_failed",
-      file_token:
-        
".eJw9yUsOgCAMBcC7cAB7JPISizR82kCJcnvjxtUsJlDWxlQwPEvhjU7TV0pk27N2goxU9f7lB80qxxPXJF-uQ1CjY5avI0wO2-EFouohiw.fhdU5u0Pb7lElEd-AUUXqjHSsdo",
-      fileloc: "/airflow/dags/tutorial_taskflow_api_failed.py",
-      has_import_errors: false,
-      has_task_concurrency_limits: false,
-      is_favorite: false,
-      is_paused: false,
-      is_stale: false,
-      last_parsed_time: "2025-01-13T07:34:01.593459Z",
-      latest_dag_runs: [
-        {
-          dag_id: "tutorial_taskflow_api",
-          end_date: "2025-01-13T04:34:12.143831Z",
-          id: 2,
-          logical_date: "2025-01-13T04:33:58.396323Z",
-          run_id: "manual__2025-01-13T04:33:58.387988+00:00",
-          start_date: "2025-01-13T04:33:58.496197Z",
-          state: "success",
-        },
-      ],
-      max_active_runs: 16,
-      max_active_tasks: 16,
-      max_consecutive_failed_dag_runs: 0,
-      owners: ["airflow"],
-      pending_actions: [],
-      tags: [{ dag_id: "tutorial_taskflow_api_failed", name: "example" }],
-      timetable_description: "Never, external triggers only",
-    };
+    const paused = url.searchParams.get("paused");
 
     if (lastDagRunState === "success") {
       return HttpResponse.json({
@@ -97,71 +133,18 @@ export const handlers: Array<HttpHandler> = [
         dags: [failedDag],
         total_entries: 1,
       });
-    } else {
-      return HttpResponse.json({
-        dags: [failedDag],
-        total_entries: 1,
-      });
     }
+
+    const dags = filterDagsByPaused(paused);
+
+    return HttpResponse.json({
+      dags,
+      total_entries: dags.length,
+    });
   }),
   http.get("/api/v2/dags", ({ request }) => {
     const url = new URL(request.url);
     const lastDagRunState = url.searchParams.get("last_dag_run_state");
-    const failedDag = {
-      dag_display_name: "tutorial_taskflow_api_failed",
-      dag_id: "tutorial_taskflow_api_failed",
-      description: null,
-      file_token:
-        
".eJw9yUsOgCAMBcC7cAB7JPISizR82kCJcnvjxtUsJlDWxlQwPEvhjU7TV0pk27N2goxU9f7lB80qxxPXJF-uQ1CjY5avI0wO2-EFouohiw.fhdU5u0Pb7lElEd-AUUXqjHSsdo",
-      fileloc: "/airflow/dags/tutorial_taskflow_api_failed.py",
-      has_import_errors: false,
-      has_task_concurrency_limits: false,
-      is_favorite: false,
-      is_paused: false,
-      is_stale: false,
-      last_expired: null,
-      last_parsed_time: "2025-01-13T06:45:33.009609Z",
-      max_active_runs: 16,
-      max_active_tasks: 16,
-      max_consecutive_failed_dag_runs: 0,
-      next_dagrun: null,
-      next_dagrun_create_after: null,
-      next_dagrun_data_interval_end: null,
-      next_dagrun_data_interval_start: null,
-      owners: ["airflow"],
-      pending_actions: [],
-      tags: [{ dag_id: "tutorial_taskflow_api_failed", name: "example" }],
-      timetable_description: "Never, external triggers only",
-      timetable_summary: null,
-    };
-
-    const successDag = {
-      dag_display_name: "tutorial_taskflow_api_success",
-      dag_id: "tutorial_taskflow_api_success",
-      description: null,
-      file_token:
-        
".eJw9yUsOgCAMBcC7cAB7JPISizR82kCJcnvjxtUsJlDWxlQwPEvhjU7TV0pk27N2goxU9f7lB80qxxPXJF-uQ1CjY5avI0wO2-EFouohiw.fhdU5u0Pb7lElEd-AUUXqjHSsdo",
-      fileloc: "/airflow/dags/tutorial_taskflow_api_success.py",
-      has_import_errors: false,
-      has_task_concurrency_limits: false,
-      is_favorite: true,
-      is_paused: false,
-      is_stale: false,
-      last_expired: null,
-      last_parsed_time: "2025-01-13T06:45:33.009609Z",
-      max_active_runs: 16,
-      max_active_tasks: 16,
-      max_consecutive_failed_dag_runs: 0,
-      next_dagrun: null,
-      next_dagrun_create_after: null,
-      next_dagrun_data_interval_end: null,
-      next_dagrun_data_interval_start: null,
-      owners: ["airflow"],
-      pending_actions: [],
-      tags: [{ dag_id: "tutorial_taskflow_api_success", name: "example" }],
-      timetable_description: "Never, external triggers only",
-      timetable_summary: null,
-    };
 
     if (lastDagRunState === "failed") {
       return HttpResponse.json({
diff --git 
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.test.tsx
 
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.test.tsx
new file mode 100644
index 00000000000..4a28ebe8087
--- /dev/null
+++ 
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.test.tsx
@@ -0,0 +1,73 @@
+/*!
+ * 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 "@testing-library/jest-dom";
+import { render, screen, waitFor } from "@testing-library/react";
+import { describe, it, expect, vi } from "vitest";
+
+import { AppWrapper } from "src/utils/AppWrapper";
+
+const mockConfig: Record<string, unknown> = {
+  auto_refresh_interval: 3,
+  default_wrap: false,
+  enable_swagger_ui: true,
+  hide_paused_dags_by_default: true,
+  instance_name: "Airflow",
+  multi_team: false,
+  page_size: 15,
+  require_confirmation_dag_change: false,
+  test_connection: "Disabled",
+};
+
+vi.mock("src/queries/useConfig", () => ({
+  useConfig: (key: string) => mockConfig[key],
+}));
+
+describe("Paused filter with hide_paused_dags_by_default enabled", () => {
+  it("defaults to showing only active dags", async () => {
+    render(<AppWrapper initialEntries={["/dags"]} />);
+
+    await waitFor(() => 
expect(screen.getByText("tutorial_taskflow_api_success")).toBeInTheDocument());
+    expect(screen.queryByText("paused_dag")).not.toBeInTheDocument();
+  });
+
+  it("shows all dags after clicking All filter", async () => {
+    render(<AppWrapper initialEntries={["/dags"]} />);
+
+    await waitFor(() => 
expect(screen.getByText("tutorial_taskflow_api_success")).toBeInTheDocument());
+    expect(screen.queryByText("paused_dag")).not.toBeInTheDocument();
+
+    // There are two "All" buttons (StateFilters and PausedFilter).
+    // The second one belongs to PausedFilter.
+    const allButtons = screen.getAllByText("filters.paused.all");
+
+    allButtons[1]?.click();
+    await waitFor(() => 
expect(screen.getByText("paused_dag")).toBeInTheDocument());
+    
expect(screen.getByText("tutorial_taskflow_api_success")).toBeInTheDocument();
+  });
+
+  it("shows only paused dags after clicking Paused filter", async () => {
+    render(<AppWrapper initialEntries={["/dags"]} />);
+
+    await waitFor(() => 
expect(screen.getByText("tutorial_taskflow_api_success")).toBeInTheDocument());
+
+    screen.getByText("filters.paused.paused").click();
+    await waitFor(() => 
expect(screen.getByText("paused_dag")).toBeInTheDocument());
+    await waitFor(() => 
expect(screen.queryByText("tutorial_taskflow_api_success")).not.toBeInTheDocument());
+  });
+});
diff --git 
a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx 
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx
index 3a1ee4fdf58..e9066d8c180 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsFilters/DagsFilters.tsx
@@ -90,7 +90,7 @@ export const DagsFilters = () => {
   };
 
   const handlePausedChange = (value: BooleanFilterValue) => {
-    if (value === "all") {
+    if (value === "all" && !hidePausedDagsByDefault) {
       searchParams.delete(PAUSED_PARAM);
     } else {
       searchParams.set(PAUSED_PARAM, value);

Reply via email to