diff --git a/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.test.tsx b/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.test.tsx new file mode 100644 index 0000000000000..6315d42243592 --- /dev/null +++ b/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.test.tsx @@ -0,0 +1,55 @@ +/*! + * 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 { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { Wrapper } from "src/utils/Wrapper"; + +import { DagRunMetrics } from "./DagRunMetrics"; + +describe("DagRunMetrics", () => { + it("shows percentages when counts are not capped", () => { + render( + , + { wrapper: Wrapper }, + ); + + expect(screen.getByText("25.00%")).toBeInTheDocument(); + expect(screen.getByText("75.00%")).toBeInTheDocument(); + }); + + it("hides percentages when any state reaches the API cap", () => { + render( + , + { wrapper: Wrapper }, + ); + + expect(screen.getByText("1000+")).toBeInTheDocument(); + expect(screen.queryByText("99.30%")).not.toBeInTheDocument(); + expect(screen.queryByText("0.70%")).not.toBeInTheDocument(); + }); +}); diff --git a/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx b/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx index a6578c0f3839e..4484887cff59c 100644 --- a/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx @@ -35,6 +35,7 @@ const DAGRUN_STATES: Array = ["queued", "running", "success" export const DagRunMetrics = ({ dagRunStates, endDate, startDate, stateCountLimit }: DagRunMetricsProps) => { const { t: translate } = useTranslation(); const total = Object.values(dagRunStates).reduce((sum, count) => sum + count, 0); + const hasCappedState = Object.values(dagRunStates).some((count) => count >= stateCountLimit); return ( @@ -51,6 +52,7 @@ export const DagRunMetrics = ({ dagRunStates, endDate, startDate, stateCountLimi key={state} kind="dag_runs" runs={dagRunStates[state]} + showPercentages={!hasCappedState} startDate={startDate} state={state} total={total} diff --git a/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx b/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx index e0cfff1f01c8a..b378fc6cba493 100644 --- a/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx @@ -32,6 +32,7 @@ type MetricSectionProps = { readonly endDate?: string; readonly kind: string; readonly runs: number; + readonly showPercentages?: boolean; readonly startDate: string; readonly state: keyof TaskInstanceStateCount; readonly total: number; @@ -42,13 +43,15 @@ export const MetricSection = ({ endDate, kind, runs, + showPercentages = true, startDate, state, total, }: MetricSectionProps) => { const stateWidth = capped ? BAR_WIDTH : total === 0 ? 0 : (runs / total) * BAR_WIDTH; const remainingWidth = BAR_WIDTH - stateWidth; - const statePercent = capped ? undefined : total === 0 ? 0 : ((runs / total) * 100).toFixed(2); + const statePercent = + showPercentages && !capped && total !== 0 ? ((runs / total) * 100).toFixed(2) : undefined; const stateParam = kind === "task_instances" ? SearchParamsKeys.TASK_STATE : SearchParamsKeys.STATE; const searchParams = new URLSearchParams( @@ -74,23 +77,25 @@ export const MetricSection = ({ {statePercent === undefined ? undefined : {statePercent}% } - - - - + {showPercentages ? ( + + + + + ) : undefined} ); }; diff --git a/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/TaskInstanceMetrics.tsx b/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/TaskInstanceMetrics.tsx index f36c0a4635b3d..0bc00eda691bc 100644 --- a/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/TaskInstanceMetrics.tsx +++ b/airflow-core/src/airflow/ui/src/pages/Dashboard/HistoricalMetrics/TaskInstanceMetrics.tsx @@ -54,6 +54,7 @@ export const TaskInstanceMetrics = ({ }: TaskInstanceMetricsProps) => { const { t: translate } = useTranslation(); const total = Object.values(taskInstanceStates).reduce((sum, count) => sum + count, 0); + const hasCappedState = Object.values(taskInstanceStates).some((count) => count >= stateCountLimit); return ( @@ -73,6 +74,7 @@ export const TaskInstanceMetrics = ({ key={state} kind="task_instances" runs={taskInstanceStates[state]} + showPercentages={!hasCappedState} startDate={startDate} state={state as TaskInstanceState} total={total}