This is an automated email from the ASF dual-hosted git repository.
vatsrahul1001 pushed a commit to branch v3-4-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit ca7a88b0f3b1477ece34856b51d6f56a7a158330
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Oct 30 00:59:23 2026 +0530
[v3-4-test] Fix dashboard Active Dags filtering (#75350) (#75949)
* [v3-4-test] Fix dashboard Active Dags filtering (#75350)
(cherry picked from commit fa160f835387cc88596aa5c0ecfdf82176249487)
Co-authored-by: Anzin Mohammed <[email protected]>
* UI: Fix the Dag filters test that fails lint and vitest (#75963)
(cherry picked from commit f605f1040f3b08211d23d8cf91c5717343b25631)
---------
Co-authored-by: Anzin Mohammed <[email protected]>
Co-authored-by: Pushkal Gupta
<[email protected]>
---
.../FilterBar/filters/pausedStateParams.ts | 19 ++++++++---------
.../src/airflow/ui/src/constants/filterConfigs.tsx | 2 ++
.../src/airflow/ui/src/mocks/handlers/dags.ts | 4 ++++
.../DagsList/DagsFilters/DagsFilters.test.tsx | 24 +++++++++++++++++-----
.../src/airflow/ui/src/pages/DagsList/DagsList.tsx | 4 +---
5 files changed, 35 insertions(+), 18 deletions(-)
diff --git
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/pausedStateParams.ts
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/pausedStateParams.ts
index f7543ffe6f7..dad892a5b63 100644
---
a/airflow-core/src/airflow/ui/src/components/FilterBar/filters/pausedStateParams.ts
+++
b/airflow-core/src/airflow/ui/src/components/FilterBar/filters/pausedStateParams.ts
@@ -21,9 +21,10 @@ import { SearchParamsKeys } from
"src/constants/searchParams";
import type { FilterValue } from "../types";
/**
- * The Paused state pill covers three choices backed by the two URL params
that already exist:
+ * The Paused state pill covers four choices backed by the two URL params that
already exist:
*
* "not_paused" → ``paused=false`` unpaused, which includes
draining Dags
+ * "active" → ``scheduling_state=active`` excludes paused and
draining Dags
* "paused" → ``paused=true``
* "draining" → ``scheduling_state=draining``
*
@@ -32,29 +33,27 @@ import type { FilterValue } from "../types";
* ``paused`` for the other two leaves existing links and the
``hide_paused_dags_by_default``
* seed working untouched.
*/
-export type PausedStateValue = "draining" | "not_paused" | "paused";
+export type PausedStateValue = "active" | "draining" | "not_paused" | "paused";
export const pausedStateToSearchParams = (value: FilterValue): Record<string,
string | undefined> => {
const paused = value === "not_paused" ? "false" : undefined;
return {
[SearchParamsKeys.PAUSED]: value === "paused" ? "true" : paused,
- [SearchParamsKeys.SCHEDULING_STATE]: value === "draining" ? "draining" :
undefined,
+ [SearchParamsKeys.SCHEDULING_STATE]: value === "draining" || value ===
"active" ? value : undefined,
};
};
export const pausedStateFromSearchParams = (params: URLSearchParams):
PausedStateValue | undefined => {
const schedulingState = params.get(SearchParamsKeys.SCHEDULING_STATE);
- // Checked first: a draining Dag is also unpaused, so ``paused=false``
alongside it would
- // otherwise win and widen the pill back to every unpaused Dag.
- if (schedulingState === "draining") {
- return "draining";
+ // Scheduling state is narrower than paused=false, which also includes
draining Dags.
+ if (schedulingState === "draining" || schedulingState === "active") {
+ return schedulingState;
}
// ``scheduling_state=paused`` is the same set as ``paused=true``, so links
written against the
- // short-lived Scheduling state filter keep working. ``=active`` has no
equivalent here — it
- // excludes draining Dags, which no single choice does — so it falls through
and is ignored.
+ // short-lived Scheduling state filter keep working.
if (schedulingState === "paused") {
return "paused";
}
@@ -78,5 +77,5 @@ export const pausedStateToQuery = (value: PausedStateValue |
undefined) => {
paused = false;
}
- return { paused, schedulingState: value === "draining" ? ("draining" as
const) : undefined };
+ return { paused, schedulingState: value === "draining" || value === "active"
? value : undefined };
};
diff --git a/airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx
b/airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx
index 969abe1b089..975a75c4b3a 100644
--- a/airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx
+++ b/airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx
@@ -99,6 +99,7 @@ export const useFilterConfigs = () => {
"common",
"components",
"admin",
+ "dashboard",
"dags",
"hitl",
]);
@@ -385,6 +386,7 @@ export const useFilterConfigs = () => {
label: translate("dags:filters.pausedState"),
options: [
{ label: translate("dags:filters.paused.active"), value: "not_paused"
},
+ { label: translate("dashboard:stats.activeDags"), value: "active" },
{ label: translate("dags:draining.label"), value: "draining" },
{ label: translate("dags:filters.paused.paused"), value: "paused" },
],
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 a9f3570e6c4..41b4144c54e 100644
--- a/airflow-core/src/airflow/ui/src/mocks/handlers/dags.ts
+++ b/airflow-core/src/airflow/ui/src/mocks/handlers/dags.ts
@@ -159,6 +159,10 @@ const filterDags = ({
// Narrower than ``paused=false``: a draining Dag is unpaused, so the two
are not alternatives.
if (schedulingState === "draining") {
allDags = allDags.filter((dag) => "scheduling_state" in dag &&
dag.scheduling_state === "draining");
+ } else if (schedulingState === "active") {
+ allDags = allDags.filter(
+ (dag) => !dag.is_paused && !("scheduling_state" in dag &&
dag.scheduling_state === "draining"),
+ );
}
if (paused === "true") {
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
index e32eec62fa5..380fd374c67 100644
---
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
@@ -228,13 +228,27 @@ describe("Links written before draining moved into the
pill", () => {
await waitFor(() => expect(screen.getByText(shown)).toBeInTheDocument());
});
- it("ignores scheduling_state=active rather than filtering with no pill to
explain it", async () => {
+ it("restores the dashboard Active Dags filter and excludes paused and
draining Dags", async () => {
+ withDrainingDag();
render(<AppWrapper initialEntries={["/dags?scheduling_state=active"]} />);
- // No choice means "not paused and not draining", so the param is inert
instead of narrowing
- // the list invisibly.
- await waitFor(() =>
expect(screen.getByText("paused_dag")).toBeInTheDocument());
+ await waitFor(() =>
expect(screen.getByText("tutorial_taskflow_api_success")).toBeInTheDocument());
+
expect(screen.getByTestId("paused-pill")).toHaveTextContent("stats.activeDags");
+ expect(screen.queryByText("paused_dag")).not.toBeInTheDocument();
+ expect(screen.queryByText("draining_dag")).not.toBeInTheDocument();
+
+ await selectPillOption("paused", "not_paused");
+ await waitFor(() =>
expect(screen.getByTestId("paused-pill")).toHaveTextContent("filters.paused.active"));
+ await waitFor(() =>
expect(screen.getByText("draining_dag")).toBeInTheDocument());
+ expect(screen.queryByText("paused_dag")).not.toBeInTheDocument();
+
+ await selectPillOption("paused", "active");
+ await waitFor(() =>
expect(screen.getByTestId("paused-pill")).toHaveTextContent("stats.activeDags"));
+ await waitFor(() =>
expect(screen.queryByText("draining_dag")).not.toBeInTheDocument());
expect(screen.getByText("tutorial_taskflow_api_success")).toBeInTheDocument();
- expect(screen.queryByTestId("paused-pill")).not.toBeInTheDocument();
+
+ fireEvent.click(screen.getByTestId("paused-pill-remove"));
+ await waitFor(() =>
expect(screen.getByText("paused_dag")).toBeInTheDocument());
+ expect(screen.getByText("draining_dag")).toBeInTheDocument();
});
});
diff --git a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
index 8b1e114e679..e7e37ff9f44 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagsList/DagsList.tsx
@@ -146,9 +146,7 @@ export const DagsList = () => {
const multiTeamEnabled = Boolean(useConfig("multi_team"));
- // Derived through the pill's own mapping so the query can never filter on
more than the pill
- // shows. A legacy ``scheduling_state=active`` has no choice to represent
it, and would
- // otherwise narrow the list with nothing on screen saying why.
+ // Derived through the pill's own mapping so the query can never filter on
more than the pill shows.
const { paused, schedulingState } =
pausedStateToQuery(pausedStateFromSearchParams(searchParams));
const showFavorites = searchParams.get(FAVORITE);