diff --git a/airflow-core/src/airflow/ui/src/mocks/handlers/dag_runs.ts b/airflow-core/src/airflow/ui/src/mocks/handlers/dag_runs.ts
index ed55e14b01986..7fcb78ebfc919 100644
--- a/airflow-core/src/airflow/ui/src/mocks/handlers/dag_runs.ts
+++ b/airflow-core/src/airflow/ui/src/mocks/handlers/dag_runs.ts
@@ -38,7 +38,7 @@ const dagRunBeforeFilter = {
};
const dagRunInRange = {
- conf: null,
+ conf: { batch: 42, env: "prod" },
dag_display_name: "test_dag",
dag_id: "test_dag",
dag_run_id: "run_in_range",
diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.test.tsx b/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.test.tsx
index 50d1d9d8e6a61..3b9b19e0eabef 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.test.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.test.tsx
@@ -17,11 +17,19 @@
* under the License.
*/
import "@testing-library/jest-dom";
-import { render, screen, waitFor } from "@testing-library/react";
-import { describe, expect, it } from "vitest";
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { AppWrapper } from "src/utils/AppWrapper";
+// Stand in for the Monaco-backed JSON viewer so the test can assert the collapse
+// state without loading the editor.
+vi.mock("src/components/RenderedJsonField", () => ({
+ default: ({ collapsed }: { readonly collapsed?: boolean }) => (
+
+ ),
+}));
+
// The dag_runs mock handler (see src/mocks/handlers/dag_runs.ts) returns:
// - run_before_filter (logical_date: 2024-12-31) — excluded when filtering Jan 2025
// - run_in_range (logical_date: 2025-01-15) — included when filtering Jan 2025
@@ -46,3 +54,35 @@ describe("DagRuns logical date filter", () => {
expect(screen.queryByText("run_before_filter")).not.toBeInTheDocument();
});
});
+
+describe("DagRuns conf expand/collapse", () => {
+ beforeEach(() => {
+ // The conf column is hidden by default; reveal it so the JSON viewer renders.
+ globalThis.localStorage.setItem(
+ "dataTable:common:dagRun:columnVisibility",
+ JSON.stringify({ conf: true }),
+ );
+ });
+
+ afterEach(() => {
+ globalThis.localStorage.clear();
+ });
+
+ it("toggles conf JSON collapse state via the expand/collapse all buttons", async () => {
+ render();
+
+ await waitFor(() => expect(screen.getByTestId("rendered-json-field")).toBeInTheDocument());
+
+ expect(screen.getByTestId("rendered-json-field")).toHaveAttribute("data-collapsed", "true");
+
+ fireEvent.click(screen.getByTestId("expand-all-button"));
+ await waitFor(() =>
+ expect(screen.getByTestId("rendered-json-field")).toHaveAttribute("data-collapsed", "false"),
+ );
+
+ fireEvent.click(screen.getByTestId("collapse-all-button"));
+ await waitFor(() =>
+ expect(screen.getByTestId("rendered-json-field")).toHaveAttribute("data-collapsed", "true"),
+ );
+ });
+});
diff --git a/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.tsx b/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.tsx
index 971354ebe55b5..a7e67fadfa65f 100644
--- a/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/DagRuns/DagRuns.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Flex, HStack, Text } from "@chakra-ui/react";
+import { Flex, HStack, Text, useDisclosure } from "@chakra-ui/react";
import type { ColumnDef } from "@tanstack/react-table";
import type { TFunction } from "i18next";
import { useTranslation } from "react-i18next";
@@ -36,6 +36,7 @@ import {
} from "src/components/DataTable/useRowSelection";
import { useTableURLState } from "src/components/DataTable/useTableUrlState";
import { ErrorAlert } from "src/components/ErrorAlert";
+import { ExpandCollapseButtons } from "src/components/ExpandCollapseButtons";
import { LimitedItemsList } from "src/components/LimitedItemsList";
import { MarkRunAsButton } from "src/components/MarkAs";
import RenderedJsonField from "src/components/RenderedJsonField";
@@ -86,10 +87,11 @@ const {
type ColumnProps = {
readonly dagId?: string;
+ readonly open: boolean;
readonly translate: TFunction;
} & GetColumnsParams;
-const runColumns = ({ dagId, translate }: ColumnProps): Array> => [
+const runColumns = ({ dagId, open, translate }: ColumnProps): Array> => [
{
accessorKey: "select",
cell: ({ row }) => ,
@@ -196,7 +198,7 @@ const runColumns = ({ dagId, translate }: ColumnProps): Array
original.conf && Object.keys(original.conf).length > 0 ? (
-
+
) : undefined,
header: translate("dagRun.conf"),
},
@@ -222,6 +224,7 @@ export const DagRuns = () => {
const { t: translate } = useTranslation();
const { dagId } = useParams();
const [searchParams] = useSearchParams();
+ const { onClose, onOpen, open } = useDisclosure();
const { setTableURLState, tableURLState } = useTableURLState({
columnVisibility: {
@@ -334,6 +337,7 @@ export const DagRuns = () => {
const columns = runColumns({
dagId,
multiTeam: false,
+ open,
translate,
});
@@ -344,7 +348,16 @@ export const DagRuns = () => {
onSelectAll={handleSelectAll}
selectedRows={selectedRows}
>
-
+
+
+
+