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} > - + + + +