diff --git a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs index d5dc40768..637e8b50a 100644 --- a/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs +++ b/src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs @@ -176,10 +176,56 @@ private static bool TryGetParentFormElementSpecialCase( IHtmlButtonElement { Type: "submit", Form: not null } button => button.Form, _ => null }; + + // If form is still null, try to find it by the form attribute for submit buttons/inputs + if (form is null && element.HasAttribute("form")) + { + var isSubmitElement = element switch + { + IHtmlInputElement { Type: "submit" } => true, + IHtmlButtonElement { Type: "submit" } => true, + _ => false + }; + + if (isSubmitElement) + { + var formId = element.GetAttribute("form"); + if (!string.IsNullOrEmpty(formId)) + { + // Try to find the form element by traversing up to find a common ancestor + // and then searching down for the form + form = FindFormById(element, formId); + } + } + } return form is not null && form.TryGetEventId(Htmlizer.ToBlazorAttribute("onsubmit"), out eventId); } + + private static IHtmlFormElement? FindFormById(IElement element, string formId) + { + // Traverse up the DOM tree to find a common ancestor and search its children + // for the form with the matching ID. This handles cases where the button and + // form are siblings or in different subtrees. + var current = element.Parent as IElement; + while (current is not null) + { + // Search children of current element for the form with matching ID + foreach (var child in current.Children) + { + if ((child.Id == formId || child.GetAttribute("id") == formId) && child is IHtmlFormElement htmlForm) + { + return htmlForm; + } + } + + // Move up to parent to widen the search + current = current.Parent as IElement; + } + + return null; + } private static bool EventIsDisabled(this IElement element, string eventName) { diff --git a/tests/bunit.testassets/SampleComponents/FormWithSubmitElementsOutside.razor b/tests/bunit.testassets/SampleComponents/FormWithSubmitElementsOutside.razor new file mode 100644 index 000000000..869125474 --- /dev/null +++ b/tests/bunit.testassets/SampleComponents/FormWithSubmitElementsOutside.razor @@ -0,0 +1,13 @@ +
+ + + +@code { + public bool SubmitWasCalled { get; private set; } + + private void Callback() + { + SubmitWasCalled = true; + } +} diff --git a/tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs b/tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs index 226affdbd..7ae88ee38 100644 --- a/tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs +++ b/tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs @@ -41,4 +41,24 @@ public void ClickingOnSubmitButtonOutsideTriggersOnsubmitOfForm() cut.Instance.SubmitWasCalled.ShouldBeTrue(); } + + [Fact] + public void ClickingOnSubmitButtonOutsideWithFormAttributeTriggersOnsubmit() + { + var cut = Render