Skip to content
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

feat: implemented multi step form #3436

Merged
merged 20 commits into from
Sep 29, 2023
Merged

feat: implemented multi step form #3436

merged 20 commits into from
Sep 29, 2023

Conversation

neatbyte-vnobis
Copy link
Collaborator

Changes

Implements issue "Add the ability to build multi-step forms".

How Has This Been Tested?

Manual

Documentation

None

@neatbyte-ivelychko
Copy link

neatbyte-ivelychko commented Jul 21, 2023

Functionality is ready.
Fixed API Tests.

@neatbyte-ivelychko
Copy link

neatbyte-ivelychko commented Jul 21, 2023

@SvenAlHamad @adrians5j

I have several questions regarding Form behaviour:
1 - How many fields we can store in one row, for now there are no limitations, we can add as many fields as we want in the row, but the design looks awful. If we have 3 fields in one row it looks okay (Image 1), but if we have more than 3 fields then the fields are bleeding out (Image 2). What should we do, should we introduce limitation (like max 3 fields in the row)?

(IMAGE 1) Знімок екрана 2023-07-21 о 14 00 21

(IMAGE 2) Знімок екрана 2023-07-21 о 14 00 38

2 - Let's say that we have created 4 form step, then we swithced to the preview tab, navigated to the last step and we decided to delete last step and the question is, what should we do with the preview, should we reset all fields values and reset preview step to the first step in the list, or should we just reset preview step to the first step? As an example below now we reset our preview to the first step, whenever we remove step.

2023-07-21.14.03.02.mov

@neatbyte-ivelychko
Copy link

@adrians5j what should we do about migration? Should I implement it? Or someone else gonna do that?

@SvenAlHamad & @adrians5j also I need your opinion on my comment above #3436 (comment)

@adrians5j
Copy link
Member

Hey @neatbyte-ivelychko, thanks for the questions.

Regarding how many fields in a row...I can see the current limit is 4 fields per row, plus on a wider screen, it looks fine.

image

But, if it's not too much trouble, we can add this so we have a horizontal scrollbar on smaller screens:

image

For now I guess it's fine.

Regarding the form reset, having the form completely reset or resetting to step 1 is both fine at this point I guess.

@adrians5j
Copy link
Member

Leave the migration to us BTW.

@neatbyte-ivelychko
Copy link

neatbyte-ivelychko commented Jul 25, 2023

@adrians5j okay, got it.
I think that I will add scrollbar.

@neatbyte-vnobis
Copy link
Collaborator Author

neatbyte-vnobis commented Jul 26, 2023

Leave the migration to us BTW.

@adrians5j I have an idea - for forms import we can add support for OLD exports (without new step property).
It will allow users to import some older exports (or exports from older Webiny deploys).

It will be almost same code as an migration - just not for db records, but for import archive records

@neatbyte-ivelychko
Copy link

@adrians5j also what should we do with cypress tests, should we add more of them in this PR (I think that we need to add tests for steps and also fix already existing if they fail) or we will do that in another PR?

@SvenAlHamad
Copy link
Contributor

I did a quick test on this one and have some feedback:

  • When we add a new step, a step should have some default title, for example it can be just "Step"
  • when you drag and drop the step fields, it works in an odd way, try creating two steps, and then move the first step below the second one, you'll see that it has a strange way of working

@github-actions
Copy link

github-actions bot commented Aug 2, 2023

Cypress E2E tests have been initiated (for more information, click here). ✨

@neatbyte-ivelychko
Copy link

/cypress

@neatbyte-ivelychko
Copy link

Also I did a quick research on how to handle drag-n-drop in Cypress
Here is the link -> https://stackoverflow.com/questions/55361499/how-to-implement-drag-and-drop-in-cypress-test

console.log(source);
return;
}
const fieldId = (pos.formStep.id !== formStep.id ? pos.formStep : formStep).layout[

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will add a comment that would explain what are we doing here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

@adrians5j
Copy link
Member

adrians5j commented Aug 9, 2023

Sorry to ask, this PR can be reviewed?

Re migrations, basically, since the form data structure will become different once this PR is merged, we'll need to write a migration script that upgrades all existing form entries in the DB, so that existing data conforms with the new code. But we'll handle that. That's why I added needs-migration label to this PR, so that when we reach the release phase, I can easily see what needs migration to be written from our side.

You did mention import/exports though, and that's a good point. If it's not like a week of work, it would certainly be good to be able to import forms created with older version of Webiny into the latest version. So yeah, feel free to check that one one and ping for any further questions.

Finally, just a note on Cy tests. We've decided we'll be taking over here, so, feel free to finish any WIP Cy tests, but no need to create new ones from now on.

@neatbyte-ivelychko
Copy link

neatbyte-ivelychko commented Aug 9, 2023

@adrians5j Yeah, it is ready to be reviewed

Copy link
Member

@adrians5j adrians5j left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did the first review, managed to go over like ~60% of files.

Overall looking good, but yeah, there's always stuff to polish / improve.

Please check my comments and let me know if there will be any questions.

Once we deal with this first batch of feedback, I'll continue with the rest.

packages/api-form-builder-so-ddb/src/definitions/form.ts Outdated Show resolved Hide resolved
apps/theme/package.json Outdated Show resolved Hide resolved
// All form fields - an array of rows where each row is an array that contain fields.
const fields = getFields();
// @ts-ignore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to check this ts-ignore here.

import { Form } from "@webiny/form";
import React, { useState, useEffect } from "react";
import { Form, FormAPI } from "@webiny/form";
import { ButtonDefault, ButtonPrimary } from "@webiny/ui/Button";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, the form layout was imagined as an as lightweight as possible code. Meaning, we had SubmitButton in there, totally coded from scratch and not using @webiny/ui package.

We need to continue the same approach here.

setCurrentStep(prevStep => (prevStep += 1));
};

const handlePrevStep = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to handle step back / step fwd controls and state internally, within the app-form-builder package's component. The same thing we're doing for example with submit, pulled from FormLayoutComponent. So, let's add those controls above.

The idea is to have these FB layouts as straightforward as possible, and be able to provide as much of OOTB functionality as possible. With this approach, if the user wanted to create another layout, they'd need to c/p again the steps state management code.

packages/app-form-builder/src/types.ts Show resolved Hide resolved
console.log(source);
return;
}
const fieldId = (pos.formStep.id !== formStep.id ? pos.formStep : formStep).layout[
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

@neatbyte-ivelychko
Copy link

@adrians5j I've implemented first part of suggested changes.

@neatbyte-vnobis
Copy link
Collaborator Author

/cypress

@github-actions
Copy link

Cypress E2E tests have been initiated (for more information, click here). ✨

Copy link
Member

@adrians5j adrians5j left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another iteration done. Thanks for the work you did here so far.

With code comments, here are a couple of comments related to UI / manual testing I did.

1.Styling of "Steps" In Editor

Should we adjust the styling here a bit @SvenAlHamad ? I'm thinking the same thing we have in our CMS? Check sshot.

image

I also like how this looks:
image

In our case, this could be the step name.

2. Step Name Modal Improvements

Please check sshot.

image

3. Add Step Button

Not sure how to improve here.. IDK, but it looks a bit weird. Maybe @SvenAlHamad has an idea how to improve this?

image

apps/theme/layouts/forms/DefaultFormLayout.tsx Outdated Show resolved Hide resolved
// Validate fields for current step with "form.validateInput" function,
// if current step is invalid then we should block posibility to move to the next step,
// but if step is valid then user can switch to the next step.
const validateCurrentStepFields = (form: FormAPI) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also have this as an internal function users can immediately use.

But one thing though... When you call submit (the one passed by the Form component, L122), the rendered fields will be automatically validated. And then the Form component only calls your onSubmit callback if the validation is successful.

In other words, the validation of fields should probably be:

  1. handled by the @webiny/app-form-builder's Form component internally.
  2. should be internally handled by the submit callback / the @webiny/form package which triggers it automatically for us.

Hope I didn't miss anything here.

// we will simpy change currentStep to the first step.
useEffect(() => {
setCurrentStep(0);
}, [data.steps]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be more precise, we could also do data.steps.length here, right?

const getFields = (): FormRenderFbFormModelField[][] => {
const fieldLayout = cloneDeep(layout);
// We need to have "stepIndex" prop in order to get corresponding fields for the current step.
const getFields = (stepIndex: number): FormRenderFbFormModelField[][] => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's have stepIndex = 0 here, just so if a user knows he's working with single-step forms, he can just call getFields() and that's it.

await updateRevision({ revision: id, data: { fields } });
await updateRevision({
revision: id,
data: { fields, steps: [{ title: "", layout: [] }] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing this test here, not only it might be better to have a title here just saying "Test step" at lest, but also maybe have validation that ensures title is not an empty string. Can we try to see if this is easily doable and if so, can we do it?

@@ -170,6 +187,12 @@ describe("Forms Submission Security Test", () => {
...formA,
savedOn: expect.stringMatching(/^20/),
fields: expect.any(Array),
steps: [
{
title: null,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confused a bit why we sometimes have "", and sometimes null here. Am I missing something here?

But in any case, if we add the "required" validator I mentioned in my prev comments, this should no longer be an issue.

@neatbyte-ivelychko
Copy link

neatbyte-ivelychko commented Aug 15, 2023

@SvenAlHamad could you please take a look at these comments #3436 (review)

@adrians5j
Copy link
Member

adrians5j commented Aug 15, 2023

Tip: a couple of UI-related questions for you @SvenAlHamad above.

@neatbyte-vnobis
Copy link
Collaborator Author

@SvenAlHamad please take a look on the questions above

@SvenAlHamad
Copy link
Contributor

With regards to the UI questions above on #1 and #2 I agree with Adrian's suggestions. As for #3, I would just switch places between the icon and the text, so the text is on the right and icon on the left. This is similar to the design we have for dynamic zone in HCMS, so I'm ok with keeping this style instead of adding a button.

@neatbyte-ivelychko
Copy link

@adrians5j I have pushed the latest requested changes

Copy link
Member

@adrians5j adrians5j left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was about to merge this, but while manually testing, I noticed we can no longer move form fields side by side, for example:

image

With the new code, I just get above/below drop zones, not the left/right:
image

We should check this out before merging.

@neatbyte-ivelychko
Copy link

neatbyte-ivelychko commented Sep 26, 2023

@adrians5j Maybe you were moving row instead of field? Because we cannot drop rows in rows.
I just checked and I can move fields in fields.

Here is the demo:
https://github.com/webiny/webiny-js/assets/129154672/3fe35890-713f-49d1-9287-1595313dad32

@neatbyte-ivelychko
Copy link

@adrians5j so after merging latest next, api test for form builder are failing because of prerendering, should we fix them right now? Because before merging they were working

@adrians5j
Copy link
Member

It's an issue that was introduced by the FB prerendering PR yes. We both missed failing API tests before merging.

I'll see now if I can take care of it quickly.

@adrians5j adrians5j merged commit d512ba4 into next Sep 29, 2023
59 checks passed
brunozoric pushed a commit that referenced this pull request Oct 10, 2023
Co-authored-by: adrians5j <adrian@webiny.com>
Co-authored-by: Adrian Smijulj <adrian1358@gmail.com>
@adrians5j adrians5j deleted the neatbyte/multi-steps-form branch November 20, 2023 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants