Skip to content

Commit 3bbcc32

Browse files
authored
Merge pull request #1713 from adobe/FORMS-21742
Feat: added the loader to avoid multiple submits and tests
1 parent b839caa commit 3bbcc32

File tree

4 files changed

+80
-6
lines changed

4 files changed

+80
-6
lines changed

ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/actions/submit/v1/submit/README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Adaptive Form Submit Button component written in HTL.
2323
* Custom description/tooltip for help
2424
* Out of the box Submit rule in the button to submit the form
2525
* Allows replacing this component with other component (as mentioned below).
26+
* Shows a loader on the form container during submission
2627

2728
### Use Object
2829
The submit button component uses the `com.adobe.cq.forms.core.components.models.form.Button` Sling Model for its Use-object.
@@ -41,6 +42,14 @@ The button has a default property of `buttonType` set to `submit` which is used
4142
The component provides a `core.forms.components.button.v1.runtime` client library category that contains the Javascript runtime for the component.
4243
It should be added to a relevant site client library using the `embed` property.
4344

45+
### Loader behavior on submit
46+
47+
When the Submit button is activated and the form begins submission, the form container gets a loading state so users receive visual feedback:
48+
49+
- The form element `form.cmp-adaptiveform-container` toggles the CSS class `cmp-adaptiveform-container--submitting` for the duration of the network request.
50+
- The loading class is removed and the loader hides when validation fails, the submission succeeds, or an error occurs.
51+
52+
4453
## BEM Description
4554
```
4655
BLOCK cmp-adaptiveform-button
@@ -83,7 +92,4 @@ We support replace feature that allows replacing Reset Button component to any o
8392
* **Vendor**: Adobe
8493
* **Version**: v1
8594
* **Compatibility**: Cloud
86-
* **Status**: production-ready
87-
88-
89-
95+
* **Status**: production-ready

ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/button/v1/button/clientlibs/site/js/buttonview.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,50 @@
3636
tooltipDiv: `.${Button.bemBlock}__shortdescription`
3737
};
3838

39+
constructor(params) {
40+
super(params);
41+
const formModel = this.formContainer?.getModel?.();
42+
if (!formModel) { return; }
43+
44+
formModel.subscribe((action) => { // hide loader if validation fails (no submit performed)
45+
const errors = action?.payload;
46+
if (Array.isArray(errors) && errors.length > 0) {
47+
this._hideLoader();
48+
}
49+
}, 'validationComplete');
50+
51+
formModel.subscribe(() => { // hide loader on submit success
52+
this._hideLoader();
53+
}, 'submitSuccess');
54+
55+
formModel.subscribe(() => { // hide loader on submit error
56+
this._hideLoader();
57+
}, 'submitError');
58+
}
59+
60+
_container = null;
61+
62+
_getContainer() {
63+
if (this._container) return this._container;
64+
const container = this.formContainer?.getFormElement?.();
65+
if (container) this._container = container;
66+
return this._container;
67+
}
68+
69+
_showLoader() {
70+
const container = this._getContainer();
71+
if (container) {
72+
container.classList.add('cmp-adaptiveform-container--submitting');
73+
}
74+
}
75+
76+
_hideLoader() {
77+
const container = this._getContainer();
78+
if (container) {
79+
container.classList.remove('cmp-adaptiveform-container--submitting');
80+
}
81+
}
82+
3983
getQuestionMarkDiv() {
4084
return this.element.querySelector(Button.selectors.qm);
4185
}
@@ -70,6 +114,9 @@
70114
if (this.widget.type === 'submit' || this.widget.type === 'reset') {
71115
event.preventDefault();
72116
}
117+
if (this.widget.type === 'submit') {
118+
this._showLoader();
119+
}
73120
this._model.dispatch(new FormView.Actions.Click());
74121
});
75122
}
@@ -79,4 +126,4 @@
79126
return new Button({element, formContainer})
80127
}, Button.selectors.self);
81128

82-
})();
129+
})();

ui.af.apps/src/main/content/jcr_root/apps/core/fd/components/form/container/v2/container/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Adaptive Form container written in HTL.
2525
* Thank you message
2626
* Ability to drop other adaptive form components
2727
* Auto save feature for Drafts
28+
* Loader overlay during form submission to prevent multiple submits
2829

2930
### Use Object
3031
The Adaptive Form Container component uses the `com.adobe.cq.forms.core.components.models.form.FormContainer` Sling Model for its Use-object.
@@ -86,7 +87,9 @@ BLOCK cmp-adaptiveform-container
8687

8788
Apply a `data-cmp-is="adaptiveFormContainer"` attribute to the `cmp-adaptiveform-container` block to enable initialization of the JavaScript component.
8889

89-
Applying `data-cmp-adaptiveform-container-loader` attribute to the div specifically for applying the loader class on it, it is to ensure that the loading icon should not appear over components.
90+
Applying `data-cmp-adaptiveform-container-loader` attribute to the div specifically for applying the loader class on it, it is to ensure that the loading icon should not appear over components.
91+
92+
During the form submission, the form element `form.cmp-adaptiveform-container` toggles the class `cmp-adaptiveform-container--submitting` and the loader inside becomes visible. The class is removed once validation fails, submission succeeds or errors out, or the thank you page/message is shown.
9093

9194
Applying `data-cmp-custom-functions-module-url` attribute to the div to point to the edge delivery URL of the custom functions file. Custom Functions exported from this file will be registered in Function Runtime.
9295
This Url should whitelist the AEM author/publish domain in the Cross Origin Resource Sharing (CORS) configuration.

ui.tests/test-module/specs/actions/submit/submit.runtime.cy.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,22 @@ describe("Form with Submit Button", () => {
197197
cy.get(`.cmp-adaptiveform-button__widget`).should('have.attr', 'type', 'submit');
198198
});
199199
}
200+
201+
it("Loader shows on submit and hides on success with thank you", () => {
202+
cy.previewForm(customSubmitPagePath);
203+
204+
let requestStarted = false;
205+
cy.intercept('POST', '**/adobe/forms/af/submit/*', (req) => {
206+
requestStarted = true;
207+
return Cypress.Promise.delay(3000).then(() => req.continue())
208+
}).as('afSubmission');
209+
210+
cy.get(`.cmp-adaptiveform-button__widget`).click();
211+
212+
cy.get('form.cmp-adaptiveform-container').should('have.class', 'cmp-adaptiveform-container--submitting')
213+
214+
cy.wait('@afSubmission').then(() => {
215+
cy.contains("Thank you for submitting the form.");
216+
});
217+
});
200218
})

0 commit comments

Comments
 (0)