-
Notifications
You must be signed in to change notification settings - Fork 34
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
Make pre-submit privacy agreement more generic and configurable #209
Conversation
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.
This approach makes sense to me, so far! Curious to see how we'll update the fieldName
throughout. We'll probably also want to add some docs on how this works too.
return ( | ||
<div> | ||
{preSubmitInfo.notice || ''} | ||
{preSubmitInfo.label && |
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.
It's smart to use a field to trigger whether or not the checkbox is shown at all, but I think there is value in making this field explicit, creating an additional prop called showCheckbox
or something like that. I think it makes it more clear to the user what conditions cause the checkbox to be shown.
src/js/state/reducers.js
Outdated
[SET_PRIVACY_AGREEMENT]: (state, action) => { | ||
return _.set('data.privacyAgreementAccepted', action.privacyAgreementAccepted, state); | ||
[SET_PRE_SUBMIT]: (state, action) => { | ||
return _.set('data.preSubmitAccepted', action.preSubmitAccepted, state); |
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.
data.preSubmitAccepted
may need to change if we change fieldName
? I'm curious how we're going to set this to a variable, because the user could name this field anything they want in the form config, right?
import React from 'react'; | ||
import ErrorableCheckbox from './ErrorableCheckbox'; | ||
|
||
// formConfig.preSubmitInfo = { |
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.
It might be useful to add some comments about how preSubmitInfo
comes from the form config, and how the other props (onChange
, checked
, showError
) get passed, to distinguish them from one another.
I think I've cleaned this up so that it's ready, and I added some docs that could use a review. No hurry on landing it tho. |
@@ -419,7 +419,17 @@ For the code implementation, see the [`review` folder](../../src/js/review). | |||
|
|||
### Required checkbox before form submission | |||
|
|||
Use this feature to require a user to indicate they have read terms and conditions, a privacy policy, or any other text before submitting your form. This includes a checkbox and short-form text that can include relevant links to more verbose text on separate pages on your site. | |||
Use this feature to require a user to indicate they have read terms and conditions, a privacy policy, or any other text before submitting your form. It optionally includes a checkbox and short-form text that can include relevant links to more verbose text on separate pages on your site. To configure this feature, place a `preSubmitInfo` object in the `formConfig`: |
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.
It optionally includes a checkbox and short-form text
Which part of this is optional? By my understanding, the checkbox itself was already optional, but now the text that accompanies it is what's customizable, is that correct?
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.
Before this PR the checkbox was required and it had to be checked before the user could submit successfully. Also, the notice was hard-wired text that couldn't be changed by the user.
With this PR the user can change the notice text or leave it out completely (by not specifying the notice
property), and have a mandatory checkbox+label or leave it out completely (by setting required: false
or leaving out the required
/field
/label
properties entirely). The two features can be used independently, so you can just have a notice over the submit button for example that says "By submitting this form I agree to the privacy policy and represent that all the information here is true to the best of my knowledge."
I just added the required
field this go-round and it brings up some unusual error cases because you can now say required: true
but what if you don't specify the label
or field
? For now just pick some defaults to keep the ball rolling but this is more of a form developer error and we haven't addressed those issues although #176 is open to address such things.
I chose the settings in the example config to match the screen shot so we wouldn't need to shoot it again, but I'll need to verify that is actually true and there aren't any layout issues or whatever.
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 we need the "optionally" here, then. Unfortunately, as it was originally written, it already seemed like the feature was optional. We can just go with:
Use this feature to require a user to indicate they have read terms and conditions, a privacy policy, or any other text before submitting your form. It includes a checkbox and short-form text that can include relevant links to more verbose information on separate pages on your site. To configure this feature, place a
preSubmitInfo
object in theformConfig
:
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.
Thanks for keeping docs in mind!
@annekainicUSDS this should be ready for review. I feel like the mocking of so many things in all of the tests is a sign that the architecture does not encapsulate things very well but that can wait for a later refactor. |
It looks like you have a merge conflict here, @dmethvin-gov. |
576ee3c
to
6e3f673
Compare
it was close to working and I managed to mess it up while resolving this spurious conflict with the map file. This is a result of #218 where our build artifacts don't track the real source files. I'll mess with it and try to get something I can commit. |
@annekainicUSDS This should be ready for review now, I cleaned up the tests a bit in the process. |
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.
A few questions for you, @dmethvin-gov!
@@ -419,7 +419,17 @@ For the code implementation, see the [`review` folder](../../src/js/review). | |||
|
|||
### Required checkbox before form submission | |||
|
|||
Use this feature to require a user to indicate they have read terms and conditions, a privacy policy, or any other text before submitting your form. This includes a checkbox and short-form text that can include relevant links to more verbose text on separate pages on your site. | |||
Use this feature to require a user to indicate they have read terms and conditions, a privacy policy, or any other text before submitting your form. It includes a checkbox and short-form text that can include relevant links to more verbose information on separate pages on your site. To configure this feature, place a `preSubmitInfo` object in the `formConfig`: |
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.
This is super nit picky, but I wonder if we should move the last sentence in this paragraph that starts with "To configure this feature" to a new line, so that the instructions for how to use it are a little easier to find and more apparent.
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.
Should we also specify that the object takes specific elements, listed below? Just so they know what goes in it and that you can't just pass anything in there?
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.
At the moment you can pass anything, just like the rest of formConfig
we're not validating it against a schema so anything that is not specified is ignored. The example here is showing all the valid properties and what they might be set to.
let isValid; | ||
let errors; | ||
|
||
// If a pre-submit agreement was specified, it has to be accepted first |
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.
This logic took me a little while to reason through and understand. Perhaps a more descriptive comment would be helpful here? Something like:
"If a pre-submit agreement was passed to the form config AND if it is a required field, capture the field name. We use this to check in the form data if the field with that name has been submitted as true
before checking if the entire form submission is valid."
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.
Sounds good, I'll make a followup PR.
// If a pre-submit agreement was specified, it has to be accepted first | ||
const preSubmitField = formConfig.preSubmitInfo && | ||
formConfig.preSubmitInfo.required && (formConfig.preSubmitInfo.field || 'AGREED'); | ||
if (preSubmitField && !form.data[preSubmitField]) { |
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.
Am I understanding the addition of this logic to be pulling this check out of isValidForm
and instead checking it first here before running the isValidForm
check? Is there a reason for pulling the preSubmitInfo check out of the entire isValidForm
check?
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 we left that check in isValidForm
we'd need to pass in the information about the preSubmit checkbox and its value just so it could check them. If we weren't treating this checkbox specially, for example if it were just a plain required checkbox with an error message, I think it would be natural to let isValidForm
deal with it. However, since this is a special component that is actually part of SubmitController it seems unnatural to pass this information to a utility function to check it. It didn't stick out as much before since the name of the field and the value (true
) was hard-wired, but it still was a special case.
submission | ||
} = this.props; | ||
return ( | ||
<div> | ||
<p><strong>Note:</strong> According to federal law, there are criminal penalties, including a fine and/or imprisonment for up to 5 years, for withholding information or for providing incorrect information. (See 18 U.S.C. 1001)</p> | ||
<PrivacyAgreement | ||
{ this.preSubmitInfo && <PreSubmitSection |
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.
Is this check supposed to be formConfig.preSubmitInfo
instead of this.preSubmitInfo
? I can't figure out what this.preSubmitInfo
is referring to?
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.
Yipes, that looks like a mistake. It's completely possible that the tests aren't catching this, given that I've found the same problem in several other cases.
checked={privacyAgreementAccepted} | ||
showError={showPrivacyAgreementError}/> | ||
preSubmitInfo={formConfig.preSubmitInfo} | ||
onChange={() => this.props.setPreSubmit(formConfig.preSubmitInfo.field, this.value)} |
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'm also a little confused where this.value
comes from. In the past I've seen us use event.target.value
to grab the value of a field, which is what I assume we're trying to grab here?
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'll look at this as well.
// Initialize the preSubmit flag if one was specified | ||
const preSubmitInfo = formConfig.preSubmitInfo; | ||
if (preSubmitInfo && preSubmitInfo.field) { | ||
pageAndDataState.data[preSubmitInfo.field] = false; |
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.
So this is just adding the field to the form data and initializing it to false? Is there a reason for doing this separately as opposed to how we were doing it before where it was just adding above in the data
object?
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.
When this field had a hardwired name it could easily be initialized above. Now that the name can be configured and the field doesn't even need to be present in some cases it gets messier to try and define it in the initial object. I could have done it but it wouldn't be that readable IMO.
The only reason I initialized this field at all is because there are tests that demand a false
initial value and I was hesitant to change existing tests. However, given my experience with the tests I'm more inclined to change them now.
What I would prefer to do is not initialize this field at all since we only really care if the value is true
if the formConfig
indicates it's required. The form cannot be submitted unless the value is true
. An undefined
or missing field is not true
which works fine and it saves a bunch of ugly code like this.
* Followup to review on #209. Improve docs; refactor tests * Move to enzyme for everything in SubmitController test * Add test for just a notice with no checkbox * Clarify docs; make sample match the existing screen shot * Changes based on review feedback
…#209) * Make pre-submit privacy agreement more generic and configurable * Make preSubmit field name configurable; fix unit tests * Remove debugging; add docs * Fix second unit test with orphaned expect() * Fix failing unit test * Add test case for when preSubmitInfo isn't specified (cherry picked from commit d3cd6f0)
Essentially, make the pre-submit notification, checkbox, label, and error message configurable via the
formConfig.preSubmitInfo
. If someone wants to leave out the validation completely they just leave out thelabel
of the field (another trigger field can be chosen). So for example, you could fill out thenotice
to say, "By submitting this form you agree to the privacy agreement" and not have the checkbox or label.I still need to make a change to actually set the form field name tofieldName
in the outgoing data but figured I'd wait until we decided whether this should land and if so what the right names would be. (In other words, the current starter app always sendsprivacyAgreementAccepted: true
in the submitted form data, but with this PR if they useformConfig.preSubmitInfo: { fieldName: 'licenseAccepted' }
it should sendlicenseAccepted: true
instead.)This is passing the current unit tests but I think that's more an indictment of the unit tests than an assurance I got it right. 😸 Like the other cleanup I did to the unit tests, this file has a lot of unnecessary duplication and I just piled mine on top for now.
I'll gladly change the field and variable names used here too, I'm not super attached to them.
This fixes #53