This is an automated email from the ASF dual-hosted git repository.

bbovenzi pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-3-test by this push:
     new 32959e679f1 [v3-3-test] UI: Show an empty object for object params 
with no value (#71710) (#71876)
32959e679f1 is described below

commit 32959e679f1fc0185d35b7f4e6ce1fc5b3a2e8b4
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Aug 20 08:32:43 2026 -0400

    [v3-3-test] UI: Show an empty object for object params with no value 
(#71710) (#71876)
    
    A param declared as an object but left without a value pre-filled the
    trigger form's JSON editor with an empty array, so anyone editing it
    started from the wrong JSON literal for the field's declared type.
    (cherry picked from commit 0cfc4ef1e2dbd65d4a650cd800bb1b2c9e9934b2)
    
    Co-authored-by: Kole Harvey <[email protected]>
---
 .../components/FlexibleForm/FieldObject.test.tsx   | 88 ++++++++++++++++++++++
 .../ui/src/components/FlexibleForm/FieldObject.tsx |  2 +-
 2 files changed, 89 insertions(+), 1 deletion(-)

diff --git 
a/airflow-core/src/airflow/ui/src/components/FlexibleForm/FieldObject.test.tsx 
b/airflow-core/src/airflow/ui/src/components/FlexibleForm/FieldObject.test.tsx
new file mode 100644
index 00000000000..7afe79e551c
--- /dev/null
+++ 
b/airflow-core/src/airflow/ui/src/components/FlexibleForm/FieldObject.test.tsx
@@ -0,0 +1,88 @@
+/*!
+ * 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 } from "@testing-library/react";
+import { describe, it, expect, beforeEach, vi } from "vitest";
+
+import { Wrapper } from "src/utils/Wrapper";
+
+import { FieldObject } from "./FieldObject";
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const mockParamsDict: Record<string, any> = {};
+const mockSetParamsDict = vi.fn();
+
+vi.mock("src/queries/useParamStore", () => ({
+  paramPlaceholder: {
+    schema: {},
+    value: null,
+  },
+  useParamStore: () => ({
+    disabled: false,
+    paramsDict: mockParamsDict,
+    setParamsDict: mockSetParamsDict,
+  }),
+}));
+
+vi.mock("src/components/MonacoEditor", () => ({
+  default: ({
+    onChange,
+    value,
+  }: {
+    readonly onChange?: (value: string | undefined) => void;
+    readonly value?: string;
+  }) => (
+    <textarea aria-label="JSON editor" onChange={(event) => 
onChange?.(event.target.value)} value={value} />
+  ),
+}));
+
+vi.mock("src/context/colorMode", () => ({
+  useMonacoTheme: () => ({ beforeMount: vi.fn(), theme: "airflow-light" }),
+}));
+
+describe("FieldObject", () => {
+  beforeEach(() => {
+    Object.keys(mockParamsDict).forEach((key) => {
+      // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+      delete mockParamsDict[key];
+    });
+  });
+
+  it("renders an empty object for an object param with no value", () => {
+    mockParamsDict.test_param = {
+      schema: { type: ["object", "null"] },
+      value: null,
+    };
+
+    render(<FieldObject name="test_param" onUpdate={vi.fn()} />, { wrapper: 
Wrapper });
+
+    expect(screen.getByLabelText("JSON editor")).toHaveValue("{}");
+  });
+
+  it("renders the existing value for an object param that has one", () => {
+    mockParamsDict.test_param = {
+      schema: { type: "object" },
+      value: { key: "value" },
+    };
+
+    render(<FieldObject name="test_param" onUpdate={vi.fn()} />, { wrapper: 
Wrapper });
+
+    expect(screen.getByLabelText("JSON editor")).toHaveValue(JSON.stringify({ 
key: "value" }, undefined, 2));
+  });
+});
diff --git 
a/airflow-core/src/airflow/ui/src/components/FlexibleForm/FieldObject.tsx 
b/airflow-core/src/airflow/ui/src/components/FlexibleForm/FieldObject.tsx
index e6d72132575..3fcbcd779a4 100644
--- a/airflow-core/src/airflow/ui/src/components/FlexibleForm/FieldObject.tsx
+++ b/airflow-core/src/airflow/ui/src/components/FlexibleForm/FieldObject.tsx
@@ -46,7 +46,7 @@ export const FieldObject = ({ name, namespace = "default", 
onUpdate }: FlexibleF
       editable={!disabled}
       id={`element_${name}`}
       onChange={handleChange}
-      value={JSON.stringify(param.value ?? [], undefined, 2)}
+      value={JSON.stringify(param.value ?? {}, undefined, 2)}
     />
   );
 };

Reply via email to