]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Add submit button component 21011/head
authorTiago Melo <tmelo@suse.com>
Fri, 23 Mar 2018 16:40:29 +0000 (16:40 +0000)
committerTiago Melo <tmelo@suse.com>
Fri, 23 Mar 2018 16:40:29 +0000 (16:40 +0000)
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 <tmelo@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/shared/components/components.module.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.html [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.scss [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.spec.ts [new file with mode: 0644]
src/pybind/mgr/dashboard/frontend/src/app/shared/components/submit-button/submit-button.component.ts [new file with mode: 0644]

index 7094e1e24cc52a7f06a0ab12e7b53d5fde010698..efa9144bc5706cee1442242efa0995ae1993f454 100644 (file)
@@ -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 (file)
index 0000000..860473f
--- /dev/null
@@ -0,0 +1,9 @@
+<button type="submit"
+        class="btn btn-sm btn-primary tc_submitButton"
+        [disabled]="loading"
+        (click)="submit()">
+  <ng-content></ng-content>
+  <span *ngIf="loading">
+    <i class="fa fa-spinner fa-spin fa-fw"></i>
+  </span>
+</button>
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 (file)
index 0000000..e69de29
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 (file)
index 0000000..6ff309f
--- /dev/null
@@ -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<SubmitButtonComponent>;
+
+  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 (file)
index 0000000..c9b0ac4
--- /dev/null
@@ -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();
+    }
+  }
+}