Skip to content

Commit

Permalink
fix: correct dialog form submission with enter key (#1378)
Browse files Browse the repository at this point in the history
* fix: set correct HTML structure to include buttons inside form tag, add correct submit button type, remove click event from submit button

* docs: add accessibility description how to apply the correct form submission using the enter key in dialogs
  • Loading branch information
andreassteinmann authored Feb 27, 2023
1 parent 3334e77 commit b60e0a1
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 90 deletions.
5 changes: 5 additions & 0 deletions .vscode/intershop.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,3 +198,8 @@ qwertz
appr

logformat

keyup
keydown
keypress
mouseout
72 changes: 72 additions & 0 deletions docs/guides/accessibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,78 @@ This rule ensures, that elements with click event handlers also handle at least

Requires any element with a `mouseout` event handler to also handle `blur` events, and any element with a `mouseover` event handler to also handle `focus` events.

## Form Submission using the key "Enter"

Implicit form submission using the "Enter" key is vital to assistive technologies, see also [HTML5 specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#implicit-submission).
Therefore, the `form` tag has to include an `input` of `type="submit"`, for example

```html
<form>
<label for="foo">Name:</label>
<input type="text" name="foo" id="foo" />
<input type="submit" value="Submit" />
</form>
```

or a button of type "submit"

```html
<form>
<label for="foo">Name:</label>
<input type="text" name="foo" id="foo" />
<button type="submit">Submit</button>
</form>
```

### Form submission in dialogs

Dialogs (or modals) are separated into three sections:

- modal header
- modal body
- modal footer

where the form is positioned inside the model body and the buttons are positioned inside the modal footer.
The following simplified example shows the wrong HTML structure:

:warning: **Wrong HTML structure**

```html
<div class="modal-body">
<form (ngSubmit)="submit()">
<formly-form></formly-form>
</form>
</div>
<div class="modal-footer">
<button type="button" (click)="submit()">Submit</button>
<button type="button" (click)="cancel()">Cancel</button>
</div>
```

The button with the text "Submit" calls the same function `foo()` as the form `(ngSubmit)` but the form would not be submitted using the "Enter" key because the submit button is positioned outside the `form` tag.
The following example shows the correct HTML structure:

:heavy_check_mark: **Correct HTML structure**

```html
<form (ngSubmit)="submit()">
<div class="modal-body">
<formly-form></formly-form>
</div>
<div class="modal-footer">
<button type="submit">Submit</button>
<button type="button" (click)="cancel()">Cancel</button>
</div>
</form>
```

where

- the `form` tag surrounds both the formly form (including the form elements) and the submit button
- the function `submit()` is only called at the `form` tag
- the "Submit" button is correctly defined using `type="submit"` and does not call `submit()` using `(click)=""`
- the "Cancel" button is only defined as `type="button"` to prevent any default behavior

## Further References

- [Angular A11y ESLint Rules](https://dev.to/bitovi/angular-a11y-eslint-rules-2fjc)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,23 @@ <h2 class="modal-title">{{ modalHeader | translate }}</h2>
</button>
</div>

<div class="modal-body">
<form [formGroup]="costCenterBuyerForm" (ngSubmit)="submitCostCenterBuyerForm()">
<form [formGroup]="costCenterBuyerForm" (ngSubmit)="submitCostCenterBuyerForm()">
<div class="modal-body">
<formly-form [form]="costCenterBuyerForm" [fields]="fields" [model]="model"></formly-form>
</form>
</div>
</div>

<div class="modal-footer">
<button
type="submit"
class="btn btn-primary"
[disabled]="formDisabled"
(click)="submitCostCenterBuyerForm()"
data-testing-id="cost-center-buyer-edit-dialog-submit"
>
{{ 'account.costcenter.details.buyers.action.save' | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.cancel.button.label' | translate }}
</button>
</div>
<div class="modal-footer">
<button
type="submit"
class="btn btn-primary"
[disabled]="formDisabled"
data-testing-id="cost-center-buyer-edit-dialog-submit"
>
{{ 'account.costcenter.details.buyers.action.save' | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.cancel.button.label' | translate }}
</button>
</div>
</form>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ <h2 class="modal-title">{{ 'approval.rejectform.reject_order.heading' | translat
</button>
</div>

<div class="modal-body">
<form [formGroup]="rejectForm" (ngSubmit)="submitForm()" class="clearfix">
<form [formGroup]="rejectForm" (ngSubmit)="submitForm()" class="clearfix">
<div class="modal-body">
<formly-form [form]="rejectForm" [fields]="fields"></formly-form>
</form>
</div>
</div>

<div class="modal-footer">
<button class="btn btn-primary" type="submit" (click)="submitForm()" [disabled]="formDisabled">
{{ 'approval.rejectform.button.reject.label' | translate }}
</button>
<button class="btn btn-secondary" type="button" (click)="hide()">
{{ 'approval.rejectform.button.cancel.label' | translate }}
</button>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" [disabled]="formDisabled">
{{ 'approval.rejectform.button.reject.label' | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'approval.rejectform.button.cancel.label' | translate }}
</button>
</div>
</form>
</div>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,24 @@ <h2 class="modal-title">{{ modalHeader | translate }}</h2>
</button>
</div>

<div class="modal-body">
<form [formGroup]="orderTemplateForm" (ngSubmit)="submitOrderTemplateForm()">
<form [formGroup]="orderTemplateForm" (ngSubmit)="submitOrderTemplateForm()">
<div class="modal-body">
<!-- TODO: Show server error -->
<formly-form [form]="orderTemplateForm" [fields]="fields" [model]="model"></formly-form>
</form>
</div>
</div>

<div class="modal-footer">
<button
type="submit"
class="btn btn-primary"
[disabled]="formDisabled"
(click)="submitOrderTemplateForm()"
data-testing-id="order-template-dialog-submit"
>
{{ primaryButton | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.cancel.button.label' | translate }}
</button>
</div>
<div class="modal-footer">
<button
type="submit"
class="btn btn-primary"
[disabled]="formDisabled"
data-testing-id="order-template-dialog-submit"
>
{{ primaryButton | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.cancel.button.label' | translate }}
</button>
</div>
</form>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ <h2 class="modal-title">{{ headerTranslationKey | translate }}</h2>
</div>

<ng-container *ngIf="showForm; else showSuccess">
<div class="modal-body">
<form [formGroup]="formGroup" (ngSubmit)="submitForm()">
<form [formGroup]="formGroup" (ngSubmit)="submitForm()">
<div class="modal-body">
<ish-select-order-template-form
[formGroup]="formGroup"
[addMoveProduct]="addMoveProduct"
></ish-select-order-template-form>
</form>
</div>
</div>

<div class="modal-footer">
<button type="button" class="btn btn-primary" (click)="submitForm()">
{{ submitButtonTranslationKey | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.cancel.button.label' | translate }}
</button>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">
{{ submitButtonTranslationKey | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.cancel.button.label' | translate }}
</button>
</div>
</form>
</ng-container>

<ng-template #showSuccess>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,20 @@ <h2 class="modal-title">{{ headerTranslationKey | translate }}</h2>
</div>

<ng-container *ngIf="showForm; else showSuccess">
<div class="modal-body">
<form [formGroup]="formGroup" (ngSubmit)="submitForm()">
<form [formGroup]="formGroup" (ngSubmit)="submitForm()">
<div class="modal-body">
<ish-select-wishlist-form [formGroup]="formGroup" [addMoveProduct]="addMoveProduct"></ish-select-wishlist-form>
</form>
</div>
</div>

<div class="modal-footer">
<button type="button" class="btn btn-primary" (click)="submitForm()">
{{ submitButtonTranslationKey | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.wishlists.add_to_wishlist.cancel_button.text' | translate }}
</button>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">
{{ submitButtonTranslationKey | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.wishlists.add_to_wishlist.cancel_button.text' | translate }}
</button>
</div>
</form>
</ng-container>

<ng-template #showSuccess>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,19 @@ <h2 class="modal-title">{{ modalHeader | translate }}</h2>
</button>
</div>

<div class="modal-body">
<form [formGroup]="wishListForm" (ngSubmit)="submitWishlistForm()">
<form [formGroup]="wishListForm" (ngSubmit)="submitWishlistForm()">
<div class="modal-body">
<!-- TODO: Show server error -->
<formly-form [form]="wishListForm" [fields]="fields" [model]="model"> </formly-form>
</form>
</div>
</div>

<div class="modal-footer">
<button
type="button"
class="btn btn-primary"
[disabled]="formDisabled"
(click)="submitWishlistForm()"
data-testing-id="wishlist-dialog-submit"
>
{{ primaryButton | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.wishlists.wishlist_form.cancel_button.text' | translate }}
</button>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary" [disabled]="formDisabled" data-testing-id="wishlist-dialog-submit">
{{ primaryButton | translate }}
</button>
<button type="button" class="btn btn-secondary" (click)="hide()">
{{ 'account.wishlists.wishlist_form.cancel_button.text' | translate }}
</button>
</div>
</form>
</ng-template>

0 comments on commit b60e0a1

Please sign in to comment.