This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 2a3feb98506 Fix retrigger form config sync (#65749)
2a3feb98506 is described below
commit 2a3feb98506169663a4d3c43b12d7faafb0f32b0
Author: Paul <[email protected]>
AuthorDate: Mon Apr 27 15:39:35 2026 -0700
Fix retrigger form config sync (#65749)
* UI: Fix re-trigger form config sync
* UI: Clean up re-trigger regression test
* Simplify Trigger DAG form prefill test data
* Fix retrigger form UI test selector
* Fix Trigger DAG form test lint
---
.../components/TriggerDag/TriggerDAGForm.test.tsx | 131 +++++++++++++++++++++
.../src/components/TriggerDag/TriggerDAGForm.tsx | 8 +-
2 files changed, 137 insertions(+), 2 deletions(-)
diff --git
a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.test.tsx
b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.test.tsx
new file mode 100644
index 00000000000..c739b058a87
--- /dev/null
+++
b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.test.tsx
@@ -0,0 +1,131 @@
+/*!
+ * 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 { fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+import { Wrapper } from "src/utils/Wrapper";
+
+import TriggerDAGForm from "./TriggerDAGForm";
+
+const dagParams = vi.hoisted(() => ({
+ paramsDict: {
+ message: {
+ description: "Message",
+ schema: {
+ title: "Message",
+ type: "string",
+ },
+ value: "Hello",
+ },
+ },
+}));
+
+vi.mock("react-i18next", () => ({
+ useTranslation: () => ({
+ // eslint-disable-next-line id-length
+ t: (translationKey: string) =>
+ ({
+ "configForm.advancedOptions": "Advanced Options",
+ })[translationKey] ?? translationKey,
+ }),
+}));
+
+vi.mock("src/queries/useDagParams", () => ({
+ useDagParams: () => dagParams,
+}));
+
+vi.mock("src/queries/useTogglePause", () => ({
+ useTogglePause: () => ({
+ mutate: vi.fn(),
+ }),
+}));
+
+vi.mock("../DateTimeInput", () => ({
+ DateTimeInput: ({ value = "" }: { readonly value?: string }) => (
+ <input aria-label="Logical Date" readOnly value={value} />
+ ),
+}));
+
+vi.mock("../JsonEditor", () => ({
+ JsonEditor: ({
+ onBlur,
+ onChange,
+ value = "",
+ }: {
+ readonly onBlur?: () => void;
+ readonly onChange?: (value: string) => void;
+ readonly value?: string;
+ }) => (
+ <textarea
+ aria-label="Configuration JSON"
+ onBlur={onBlur}
+ onChange={(event) => onChange?.(event.target.value)}
+ value={value}
+ />
+ ),
+}));
+
+describe("TriggerDAGForm", () => {
+ it("syncs Advanced Options JSON after Run Parameters edits in prefilled
re-trigger mode", async () => {
+ const { container } = render(
+ <TriggerDAGForm
+ dagDisplayName="Params Trigger UI"
+ dagId="example_params_trigger_ui"
+ error={undefined}
+ hasSchedule={false}
+ isPartitioned={false}
+ isPaused={false}
+ isPending={false}
+ onSubmitTrigger={vi.fn()}
+ open
+ prefillConfig={{
+ conf: {
+ message: "Original message",
+ },
+ logicalDate: undefined,
+ runId: "manual__test",
+ }}
+ />,
+ { wrapper: Wrapper },
+ );
+
+ await waitFor(() =>
+
expect(container.querySelector<HTMLInputElement>('input[name="element_message"]')).toBeInTheDocument(),
+ );
+ const messageField =
container.querySelector<HTMLInputElement>('input[name="element_message"]');
+
+ if (messageField === null) {
+ throw new Error("Expected message input to be rendered");
+ }
+
+ fireEvent.change(messageField, { target: { value: "Updated message" } });
+ fireEvent.click(screen.getByText("Advanced Options"));
+
+ await waitFor(() => {
+ const configJson = screen.getByLabelText("Configuration JSON");
+
+ if (!(configJson instanceof HTMLTextAreaElement)) {
+ throw new TypeError("Expected Configuration JSON to render as a
textarea");
+ }
+
+ expect(configJson.value).toContain('"Updated message"');
+ });
+ });
+});
diff --git
a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
index 91b2cd1daa0..a324bf4d7df 100644
--- a/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
+++ b/airflow-core/src/airflow/ui/src/components/TriggerDag/TriggerDAGForm.tsx
@@ -73,6 +73,7 @@ const TriggerDAGForm = ({
const initialParamsDict = useDagParams(dagId, open);
const { conf, initialParamDict, setConf, setInitialParamDict } =
useParamStore();
const [unpause, setUnpause] = useState(true);
+ const [hasAppliedPrefill, setHasAppliedPrefill] = useState(false);
const { mutate: togglePause } = useTogglePause({ dagId });
const { control, handleSubmit, reset, watch } =
useForm<DagRunTriggerParams>({
@@ -114,6 +115,9 @@ const TriggerDAGForm = ({
}
setConf(confString);
}
+ setHasAppliedPrefill(true);
+ } else if (!open) {
+ setHasAppliedPrefill(false);
}
}, [
prefillConfig,
@@ -128,10 +132,10 @@ const TriggerDAGForm = ({
// Automatically reset form when conf is fetched (only if no prefillConfig)
useEffect(() => {
- if (conf && !prefillConfig && open) {
+ if (conf && open && (!prefillConfig || hasAppliedPrefill)) {
reset((prevValues) => ({ ...prevValues, conf }));
}
- }, [conf, prefillConfig, open, reset]);
+ }, [conf, hasAppliedPrefill, prefillConfig, open, reset]);
const resetDateError = () => setErrors((prev) => ({ ...prev, date: undefined
}));