Error on pressing Enter in component props form

Created on 23 September 2024, 7 months ago

Overview

Hi there, I ran into a bug when using testing the current state of the Experience Builder. When entering a new value for component's Text Input field, I naturally thought about pressing "Enter" while my cursor was still in the field. This action submits the form and delivers this error

{"message":"Missing required argument \u0022ajax_form\u0022 for Request [post \/xb-field-form\/{entityTypeId}\/{entityId}]","file":"\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Exception\/Validation\/AddressValidationFailed.php","line":31,"trace":[{"file":"\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Exception\/Validation\/InvalidQueryArgs.php","line":17,"function":"fromAddrAndPrev","class":"League\\OpenAPIValidation\\PSR7\\Exception\\Validation\\AddressValidationFailed","type":"::"},{"file":"\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Validators\/QueryArgumentsValidator.php","line":60,"function":"becauseOfMissingRequiredArgument","class":"League\\OpenAPIValidation\\PSR7\\Exception\\Validation\\InvalidQueryArgs","type":"::"},{"file":"\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Validators\/QueryArgumentsValidator.php","line":44,"function":"validateQueryArguments","class":"League\\OpenAPIValidation\\PSR7\\Validators\\QueryArgumentsValidator","type":"-\u003E"},{"file":"\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/Validators\/ValidatorChain.php","line":25,"function":"validate","class":"League\\OpenAPIValidation\\PSR7\\Validators\\QueryArgumentsValidator","type":"-\u003E"},{"file":"\/var\/www\/html\/vendor\/league\/openapi-psr7-validator\/src\/PSR7\/RequestValidator.php","line":79,"function":"validate","class":"League\\OpenAPIValidation\\PSR7\\Validators\\ValidatorChain","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/modules\/contrib\/experience_builder\/src\/EventSubscriber\/ApiRequestValidator.php","line":39,"function":"validate","class":"League\\OpenAPIValidation\\PSR7\\RequestValidator","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/modules\/contrib\/experience_builder\/src\/EventSubscriber\/ApiMessageValidatorBase.php","line":74,"function":"validate","class":"Drupal\\experience_builder\\EventSubscriber\\ApiRequestValidator","type":"-\u003E"},{"file":"\/var\/www\/html\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php","line":246,"function":"onMessage","class":"Drupal\\experience_builder\\EventSubscriber\\ApiMessageValidatorBase","type":"-\u003E"},{"file":"\/var\/www\/html\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php","line":206,"function":"Symfony\\Component\\EventDispatcher\\{closure}","class":"Symfony\\Component\\EventDispatcher\\EventDispatcher","type":"::"},{"file":"\/var\/www\/html\/vendor\/symfony\/event-dispatcher\/EventDispatcher.php","line":56,"function":"callListeners","class":"Symfony\\Component\\EventDispatcher\\EventDispatcher","type":"-\u003E"},{"file":"\/var\/www\/html\/vendor\/symfony\/http-kernel\/HttpKernel.php","line":159,"function":"dispatch","class":"Symfony\\Component\\EventDispatcher\\EventDispatcher","type":"-\u003E"},{"file":"\/var\/www\/html\/vendor\/symfony\/http-kernel\/HttpKernel.php","line":76,"function":"handleRaw","class":"Symfony\\Component\\HttpKernel\\HttpKernel","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/Session.php","line":53,"function":"handle","class":"Symfony\\Component\\HttpKernel\\HttpKernel","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/KernelPreHandle.php","line":48,"function":"handle","class":"Drupal\\Core\\StackMiddleware\\Session","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/ContentLength.php","line":28,"function":"handle","class":"Drupal\\Core\\StackMiddleware\\KernelPreHandle","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/modules\/big_pipe\/src\/StackMiddleware\/ContentLength.php","line":32,"function":"handle","class":"Drupal\\Core\\StackMiddleware\\ContentLength","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/modules\/page_cache\/src\/StackMiddleware\/PageCache.php","line":106,"function":"handle","class":"Drupal\\big_pipe\\StackMiddleware\\ContentLength","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/modules\/page_cache\/src\/StackMiddleware\/PageCache.php","line":85,"function":"pass","class":"Drupal\\page_cache\\StackMiddleware\\PageCache","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/ReverseProxyMiddleware.php","line":48,"function":"handle","class":"Drupal\\page_cache\\StackMiddleware\\PageCache","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/NegotiationMiddleware.php","line":51,"function":"handle","class":"Drupal\\Core\\StackMiddleware\\ReverseProxyMiddleware","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/AjaxPageState.php","line":36,"function":"handle","class":"Drupal\\Core\\StackMiddleware\\NegotiationMiddleware","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/StackMiddleware\/StackedHttpKernel.php","line":51,"function":"handle","class":"Drupal\\Core\\StackMiddleware\\AjaxPageState","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/core\/lib\/Drupal\/Core\/DrupalKernel.php","line":709,"function":"handle","class":"Drupal\\Core\\StackMiddleware\\StackedHttpKernel","type":"-\u003E"},{"file":"\/var\/www\/html\/web\/index.php","line":19,"function":"handle","class":"Drupal\\Core\\DrupalKernel","type":"-\u003E"}]}

Here's a video where I demoed this error and subsequent conversation about fixing it:
https://drupal.slack.com/archives/C072JMEPUS1/p1727099881201889

Proposed resolution

Not sure what the best path to a solution here.

Avenues to persue, that have been recommended include:

  • Fix on the backend: by removing the ComponentPropsFrom's action attribute
  • Fix on the frontned: by teaching the Form to ignore the Enter key
  • User interface changes

    Pressing Enter shouldn't break things. I see in 🌱 [META] Define strategy for keyboard shortcuts Active there's already a place to discuss what keyboard shortcuts do. That's the best place to discuss what pressing Enter should accomplish.

🐛 Bug report
Status

Active

Component

Page builder

Created by

🇺🇸United States cosmicdreams Minneapolis/St. Paul

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @cosmicdreams
  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    Went down the road of pursuing a backend solution. Setting the action of the form to an empty string

    ComponentPropsForm::buildForm

    public function buildForm(array $form, FormStateInterface $form_state, ?FieldableEntityInterface $entity = NULL): array {
    ...
    # Remove the action attributes value to prevent the form from being
        # submitted when user presses Enter.
        $form['#action'] = '';
        return $form;
    }
    

    This did what was intended and now pressing enter doesn't directly lead to an error page. But hitting enter still "submits" the form which leads to a moment where the page reloads. After the page reloads, all my components I've placed are gone, and I have to start over.

    I don't think this solution is enough. Perhaps it does ultimatley make sense to remove the giant value in the action attribute, but it still is a bug to users.

    I think we should pursue a solution where we teach the component props form to respond differently to pressing Enter. Perhaps that's not sufficient to fix the bug, because the main thing we want to avoid is doing a full form submission.

    The main thing to figure how is where to make change. These two Form objects seem like good candidates:
    * ui/src/components/form/Form.tsx
    * ui/src/components/form/FormElement.tsx

  • 🇳🇱Netherlands balintbrews Amsterdam, NL

    While I was convinced before that this should be handled where the form is generated — the backend —, after more thinking, and mostly based on your findings, I have to realize that it may not be enough.

    I think on the frontend we should consider leveraging the HTMLFormElement: submit event, and Event: preventDefault() method to prevent the browser doing the default action upon submitting the form. It feels more semantic to me than handling a specific keypress, but I'm curious what others think.

  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    I did consider taking over the form's submit event. For now, I'm testing with only taking over the form's KeyDown event. In my local testing that has proven to be sufficient. I'm in the process of getting the tests to run to see if this is ready for review.

  • Pipeline finished with Failed
    7 months ago
    Total: 440s
    #290521
  • Pipeline finished with Failed
    7 months ago
    Total: 434s
    #290590
  • Pipeline finished with Failed
    7 months ago
    Total: 561s
    #290595
  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul
  • 🇳🇱Netherlands balintbrews Amsterdam, NL

    After re-running the pipeline, things are green now! 🟢

    Here is what I suggest we do next:

    1. Change the implementation to use the HTMLFormElement: submit event. As I mentioned before in #3, I think it's more semantic to intervene directly in the event that causes what we would like to prevent, instead of intercepting the event that leads to said event. We would like to ensure that if and when the form gets submitted, none of the default actions happens. How the form gets submitted, e.g.. as a result of the Enter keypress, is an implementation detail.
    2. Even though we already fixed the reported bug, I think we should remove the action property from the generated form markup returned in \Drupal\experience_builder\Form\ComponentPropsForm::buildForm, so the form markup doesn't express something that is not expected.
    3. Tests would be nice to validate this.

    Assigning to Wim Leers to get some feedback on the above.

  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    I've included the recommended changes from #7.

  • Pipeline finished with Failed
    7 months ago
    Total: 586s
    #290853
  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    First: thanks for the great bug report, @cosmicdreams! Can't believe we didn't spot this 😅

    🤔 Would it be more in line with the HTML spec to set method="dialog" per https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#method?

    https://caniuse.com/dialog indicates we could use <dialog>, and the current right sidebar — labeled xb-contextual-panelcould be wrapped in a <dialog> instead of a <div>?

    That seems appropriate because:

    The HTML element represents a modal or non-modal dialog box or other interactive component, such as a dismissible alert, inspector, or subwindow.

    https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog, emphasis mine.

    • #3: see above wrt <form method="dialog">.
    • #7.1: I independently observed the same, that's still present in the MR — perhaps @cosmicdreams forgot to push something in #8?
    • #7.2: Nope, the Media Library Widget continues to work fine! 👍
  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    I really like the solution of using method="dialog". Sounds like it was custom made for this situation. I'll check what I pushed recently and make sure I work this in.

  • Pipeline finished with Failed
    6 months ago
    Total: 481s
    #291424
  • Pipeline finished with Canceled
    6 months ago
    Total: 179s
    #291474
  • Pipeline finished with Failed
    6 months ago
    Total: 190s
    #291478
  • Pipeline finished with Success
    6 months ago
    Total: 485s
    #291488
  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    I'm not actually sure this still needs test coverage then, because that'd be testing browser-level functionality.

    Let's get expert feedback 🤓

  • 🇳🇱Netherlands balintbrews Amsterdam, NL

    <form method="dialog"> is beautiful! 👏🏻

    I was just thinking that a simple end-to-end test would be reassuring to make sure we don't accidentally remove it when generating the form, then face this awkward bug again. 😊

  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺

    Fair — simply triggering the Enter key should be sufficient then :)

  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    I'll take a shot at writing the test, but this will be my first one for Experience Builder. Are we talking about a cypress test?

  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    Also, I can't seem to be able to run any tests in my local development environment. I'm working through that.

  • Pipeline finished with Failed
    6 months ago
    Total: 567s
    #292664
  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul
  • 🇮🇳India utkarsh_33

    Left a few comments and also tests are still failing.Marking it NW again.

  • Pipeline finished with Canceled
    6 months ago
    Total: 68s
    #293329
  • Pipeline finished with Canceled
    6 months ago
    Total: 65s
    #293331
  • Pipeline finished with Failed
    6 months ago
    Total: 661s
    #293332
  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    Thanks for the review. I can see now that even though I can't get these tests to run locally (i'm on a Mac and xQuartz isn't working properly) I do see the test results on Gitlab. I see my use of data attributes is what is at fault here. I'm attempting to provide a more Cypress-centric implementation now.

  • Pipeline finished with Failed
    6 months ago
    Total: 687s
    #293339
  • Pipeline finished with Failed
    6 months ago
    Total: 508s
    #293407
  • Pipeline finished with Failed
    6 months ago
    Total: 530s
    #293434
  • Pipeline finished with Success
    6 months ago
    Total: 389s
    #293469
  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    Test passes, ready for review.

  • First commit to issue fork.
  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul

    @deepakkm Did you accidentally include work from another issue into mine?

  • 🇮🇳India deepakkm

    @cosmicdreams its a rebase, to actually verify one failure related to cypress test which i see is now fixed. Also it will be helpful for someone to RTBC this issue. I hope this helps.

  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul
  • 🇺🇸United States bnjmnm Ann Arbor, MI

    Good solution and tests, left a bit of feedback on the MR for commenty stuff.

  • Pipeline finished with Canceled
    6 months ago
    Total: 458s
    #294521
  • Pipeline finished with Canceled
    6 months ago
    Total: 521s
    #294544
  • Pipeline finished with Success
    6 months ago
    Total: 550s
    #294566
  • 🇺🇸United States cosmicdreams Minneapolis/St. Paul
  • First commit to issue fork.
  • Pipeline finished with Skipped
    6 months ago
    #297175
  • Pipeline finished with Skipped
    6 months ago
    #297177
  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
  • Pipeline finished with Skipped
    6 months ago
    #297204
  • 🇧🇪Belgium wim leers Ghent 🇧🇪🇪🇺
  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024