From 03fa8585ef85e74e3eb5ca90e2ed8cfd5fb89fa3 Mon Sep 17 00:00:00 2001 From: Tiago Melo Date: Fri, 23 Mar 2018 16:40:29 +0000 Subject: [PATCH] mgr/dashboard: Add submit button component This component is to be used inside a form. It will render a submit button with the given label. The button will disabled itself and show a loading icon when the user clicks it, usually initiating a request to the server, and it will stay in that state until the request is finished. To indicate that the request failed, returning the button to the enable state, you need to insert an error in the form with the 'cdSubmitButton' key. It will also check if the form is valid, when clicking the button, and will focus on the first invalid input. Signed-off-by: Tiago Melo --- .../shared/components/components.module.ts | 7 +- .../submit-button.component.html | 9 +++ .../submit-button.component.scss | 0 .../submit-button.component.spec.ts | 30 +++++++++ .../submit-button/submit-button.component.ts | 66 +++++++++++++++++++ 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.html create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.scss create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.spec.ts create mode 100644 src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.ts diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts index 7094e1e24cc..efa9144bc57 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts @@ -6,6 +6,7 @@ import { AlertModule, PopoverModule } from 'ngx-bootstrap'; import { HelperComponent } from './helper/helper.component'; import { SparklineComponent } from './sparkline/sparkline.component'; +import { SubmitButtonComponent } from './submit-button/submit-button.component'; import { ViewCacheComponent } from './view-cache/view-cache.component'; @NgModule({ @@ -18,13 +19,15 @@ import { ViewCacheComponent } from './view-cache/view-cache.component'; declarations: [ ViewCacheComponent, SparklineComponent, - HelperComponent + HelperComponent, + SubmitButtonComponent ], providers: [], exports: [ ViewCacheComponent, SparklineComponent, - HelperComponent + HelperComponent, + SubmitButtonComponent ] }) export class ComponentsModule { } diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.html b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.html new file mode 100644 index 00000000000..860473fc98d --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.html @@ -0,0 +1,9 @@ + diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.scss b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.spec.ts new file mode 100644 index 00000000000..6ff309f0762 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.spec.ts @@ -0,0 +1,30 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormGroup } from '@angular/forms'; + +import { SubmitButtonComponent } from './submit-button.component'; + +describe('SubmitButtonComponent', () => { + let component: SubmitButtonComponent; + let fixture: ComponentFixture; + + beforeEach( + async(() => { + TestBed.configureTestingModule({ + declarations: [SubmitButtonComponent] + }).compileComponents(); + }) + ); + + beforeEach(() => { + fixture = TestBed.createComponent(SubmitButtonComponent); + component = fixture.componentInstance; + + component.form = new FormGroup({}, {}); + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.ts new file mode 100644 index 00000000000..c9b0ac41645 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.ts @@ -0,0 +1,66 @@ +import { Component, ElementRef, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormGroup } from '@angular/forms'; + +import * as _ from 'lodash'; + +/** + * This component will render a submit button with the given label. + * + * The button will disabled itself and show a loading icon when the user clicks + * it, usually initiating a request to the server, and it will stay in that + * state until the request is finished. + * + * To indicate that the request failed, returning the button to the enable + * state, you need to insert an error in the form with the 'cdSubmitButton' key. + * p.e.: this.rbdForm.setErrors({'cdSubmitButton': true}); + * + * It will also check if the form is valid, when clicking the button, and will + * focus on the first invalid input. + * + * @export + * @class SubmitButtonComponent + * @implements {OnInit} + */ +@Component({ + selector: 'cd-submit-button', + templateUrl: './submit-button.component.html', + styleUrls: ['./submit-button.component.scss'] +}) +export class SubmitButtonComponent implements OnInit { + @Input() form: FormGroup; + @Output() submitAction = new EventEmitter(); + + loading = false; + + constructor(private elRef: ElementRef) {} + + ngOnInit() { + this.form.statusChanges.subscribe(() => { + if (_.has(this.form.errors, 'cdSubmitButton')) { + this.loading = false; + _.unset(this.form.errors, 'cdSubmitButton'); + this.form.updateValueAndValidity(); + } + }); + } + + submit() { + if (this.form.invalid) { + this.focusInvalid(); + return; + } + + this.loading = true; + this.submitAction.emit(); + } + + focusInvalid() { + const target = this.elRef.nativeElement.offsetParent.querySelector( + 'input.ng-invalid, select.ng-invalid' + ); + + if (target) { + target.focus(); + } + } +} -- 2.39.5