-
Notifications
You must be signed in to change notification settings - Fork 435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extract HTMLFormSubmission
tuple
#885
base: main
Are you sure you want to change the base?
Conversation
I don't love the @kevinmcconnell Additionally, changing the |
src/core/drive/form_submission.ts
Outdated
this.submitter = submission.submitter | ||
this.formData = submission.formData | ||
this.location = submission.location | ||
this.method = submission.fetchMethod |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The majority of these readonly
properties were once defined as get
-powered properties, which are part of the FormSubmission
public API. Since turbo:submit-start
and turbo:submit-end
event listeners are able to access FormSubmission
instances, it feels like we should go through a deprecation cycle before moving them to be private
or replaced by this.submission[...]
lookups.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the next version of Turbo is a major version bump to 8.0
, that might have implications for deprecation.
5ce6c08
to
b76cba8
Compare
@afcapel @kevinmcconnell are either of you able to review these changes (and #691, the PR that these changes are in service of). |
The `HTMLFormElement, HTMLElement?` [data clump][] occurs in various places across the internals of the codebase. This commit extracts that pairing into an `HTMLFormSubmission` class that knows how to properly extract various pieces of HTML Form Submission context from the pairing, like the `method`, `action`, or `target` values (which can be overridden by `formmethod`, `formaction`, and `target`, respectively). With the new extraction various call sites can be simplified to delegate to the object, with the most impactful changes being made in the `FormSubmission` class (mostly changes to the `constructor` and the removal of various dynamic properties). [data clump]: https://refactoring.guru/smells/data-clumps
b76cba8
to
813cdc1
Compare
@afcapel @kevinmcconnell I've rebased these changes to account for the migration from TypeScript. It's ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@seanpdoyle I think the change makes sense, but I'm not convinced that replacing element, submitter
argument tuples with an elementOrSubmission
argument helps readability. The method then have to figure out what the elementOrSubmission
really is, and extract the the HTML elements that it needs from there. All while those elements were readily available without any ambiguity in the calling place.
plain: "text/plain" | ||
} | ||
|
||
export function formEnctypeFromString(encoding) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can move this function to the bottom and remove the export
. It's only used in this file, further down.
if (this.submitter?.hasAttribute("formtarget") || this.form.hasAttribute("target")) { | ||
return this.submitter?.getAttribute("formtarget") || this.form.getAttribute("target") | ||
} else { | ||
return null | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (this.submitter?.hasAttribute("formtarget") || this.form.hasAttribute("target")) { | |
return this.submitter?.getAttribute("formtarget") || this.form.getAttribute("target") | |
} else { | |
return null | |
} | |
return this.submitter?.getAttribute("formtarget") || this.form.getAttribute("target") || null |
if (this.submitter?.hasAttribute("formaction")) { | ||
return this.submitter.getAttribute("formaction") || "" | ||
} else { | ||
return this.form.getAttribute("action") || formElementAction || "" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (this.submitter?.hasAttribute("formaction")) { | |
return this.submitter.getAttribute("formaction") || "" | |
} else { | |
return this.form.getAttribute("action") || formElementAction || "" | |
} | |
return this.submitter?.getAttribute("formaction") || this.form.getAttribute("action") || formElementAction || "" |
const formDataAsStrings = [...this.formData].reduce((entries, [name, value]) => { | ||
return entries.concat(typeof value == "string" ? [[name, value]] : []) | ||
}, []) | ||
|
||
return new URLSearchParams(formDataAsStrings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const formDataAsStrings = [...this.formData].reduce((entries, [name, value]) => { | |
return entries.concat(typeof value == "string" ? [[name, value]] : []) | |
}, []) | |
return new URLSearchParams(formDataAsStrings) | |
const params = new URLSearchParams() | |
for (const [name, value] of this.formData.entries()) { | |
if (typeof value === "string") params.append(name, value) | |
} |
We could extract a this.formDataAsURLParams
helper method from this bit, for symmetry with this.formData
.
if (this.method == FetchMethod.get) { | ||
mergeFormDataEntries(this.location, [...this.body.entries()]) | ||
} | ||
this.submission = submission |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can call this argument and instance var htmlSubmission
, to avoid confusion with FormSubmission
.
|
||
this.#withCurrentNavigationElement(element, () => { | ||
frame.src = url | ||
}) | ||
} | ||
|
||
proposeVisitIfNavigatedWithAction(frame, element, submitter) { | ||
this.action = getVisitAction(submitter, element, frame) | ||
proposeVisitIfNavigatedWithAction(frame, elementOrSubmission) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think overloading the second argument here is helping readability. I'd leave the arguments as frame, element, submitter
and unwrap the element
and submitter
in the only caller that uses an HTMLFormSubmission
.
#findFrameElement(element, submitter) { | ||
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target") | ||
return getFrameElementById(id) ?? this.element | ||
#findFrameElement(elementOrSubmission) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, I'd leave the arguments as (element, submitter)
and modify the one caller using an HTMLFormSubmission
. Being assertive about what the arguments are makes the code more confident and easier to follow.
const id = getAttribute("data-turbo-frame", submitter, element) || this.element.getAttribute("target") | ||
|
||
if (element instanceof HTMLFormElement && !this.#formActionIsVisitable(element, submitter)) { | ||
#shouldInterceptNavigation(elementOrSubmission) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
} | ||
|
||
#shouldRedirect(element, submitter) { | ||
#shouldRedirect(elementOrSubmission) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
The
HTMLFormElement, HTMLElement?
data clump occurs in various places across the internals of the codebase.This commit extracts that pairing into an
HTMLFormSubmission
class that knows how to properly extract various pieces of HTML Form Submission context from the pairing, like themethod
,action
, ortarget
values (which can be overridden byformmethod
,formaction
, andformtarget
, respectively).With the new extraction various call sites can be simplified to delegate to the object, with the most impactful changes being made in the
FormSubmission
class (mostly changes to theconstructor
and the removal of various dynamic properties).