Skip to content

Commit b270f6a

Browse files
Copilotegillinkdotnet
authored
feat: Support form submission from buttons and inputs outside form using form attribute (#1771)
* Initial plan * Initial investigation of form submit issue Co-authored-by: egil <105649+egil@users.noreply.github.com> * Implement support for clicking submit button outside form with form attribute Co-authored-by: egil <105649+egil@users.noreply.github.com> * Improve comments in FindFormById method Co-authored-by: egil <105649+egil@users.noreply.github.com> * Restore net10.0 target framework to all projects Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com> * Fix misleading comment about incremental DOM rendering Co-authored-by: egil <105649+egil@users.noreply.github.com> * Add test cases for input element with form attribute outside form Co-authored-by: egil <105649+egil@users.noreply.github.com> * Simplify FindFormById by removing GetElementById attempt Co-authored-by: egil <105649+egil@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: egil <105649+egil@users.noreply.github.com> Co-authored-by: linkdotnet <26365461+linkdotnet@users.noreply.github.com>
1 parent 7ca89e9 commit b270f6a

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

src/bunit/EventDispatchExtensions/TriggerEventDispatchExtensions.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,56 @@ private static bool TryGetParentFormElementSpecialCase(
176176
IHtmlButtonElement { Type: "submit", Form: not null } button => button.Form,
177177
_ => null
178178
};
179+
180+
// If form is still null, try to find it by the form attribute for submit buttons/inputs
181+
if (form is null && element.HasAttribute("form"))
182+
{
183+
var isSubmitElement = element switch
184+
{
185+
IHtmlInputElement { Type: "submit" } => true,
186+
IHtmlButtonElement { Type: "submit" } => true,
187+
_ => false
188+
};
189+
190+
if (isSubmitElement)
191+
{
192+
var formId = element.GetAttribute("form");
193+
if (!string.IsNullOrEmpty(formId))
194+
{
195+
// Try to find the form element by traversing up to find a common ancestor
196+
// and then searching down for the form
197+
form = FindFormById(element, formId);
198+
}
199+
}
200+
}
179201

180202
return form is not null
181203
&& form.TryGetEventId(Htmlizer.ToBlazorAttribute("onsubmit"), out eventId);
182204
}
205+
206+
private static IHtmlFormElement? FindFormById(IElement element, string formId)
207+
{
208+
// Traverse up the DOM tree to find a common ancestor and search its children
209+
// for the form with the matching ID. This handles cases where the button and
210+
// form are siblings or in different subtrees.
211+
var current = element.Parent as IElement;
212+
while (current is not null)
213+
{
214+
// Search children of current element for the form with matching ID
215+
foreach (var child in current.Children)
216+
{
217+
if ((child.Id == formId || child.GetAttribute("id") == formId) && child is IHtmlFormElement htmlForm)
218+
{
219+
return htmlForm;
220+
}
221+
}
222+
223+
// Move up to parent to widen the search
224+
current = current.Parent as IElement;
225+
}
226+
227+
return null;
228+
}
183229

184230
private static bool EventIsDisabled(this IElement element, string eventName)
185231
{
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<form @onsubmit="Callback" id="form1">
2+
</form>
3+
<button type="submit" form="form1" id="submit-button">Submit Button</button>
4+
<input type="submit" form="form1" id="submit-input" value="Submit Input" />
5+
6+
@code {
7+
public bool SubmitWasCalled { get; private set; }
8+
9+
private void Callback()
10+
{
11+
SubmitWasCalled = true;
12+
}
13+
}

tests/bunit.tests/EventDispatchExtensions/FormDispatchExtensionTest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,24 @@ public void ClickingOnSubmitButtonOutsideTriggersOnsubmitOfForm()
4141

4242
cut.Instance.SubmitWasCalled.ShouldBeTrue();
4343
}
44+
45+
[Fact]
46+
public void ClickingOnSubmitButtonOutsideWithFormAttributeTriggersOnsubmit()
47+
{
48+
var cut = Render<FormWithSubmitElementsOutside>();
49+
50+
cut.Find("#submit-button").Click();
51+
52+
cut.Instance.SubmitWasCalled.ShouldBeTrue();
53+
}
54+
55+
[Fact]
56+
public void ClickingOnSubmitInputOutsideWithFormAttributeTriggersOnsubmit()
57+
{
58+
var cut = Render<FormWithSubmitElementsOutside>();
59+
60+
cut.Find("#submit-input").Click();
61+
62+
cut.Instance.SubmitWasCalled.ShouldBeTrue();
63+
}
4464
}

0 commit comments

Comments
 (0)