From 461df8184753a8048bb2714e65094af9bb74fe1a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Stephan=20M=C3=BCller?= Date: Fri, 19 Jun 2020 17:22:40 +0200 Subject: [PATCH] mgr/dashboard: Fixes typeahead regression in the silence matcher MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This regression was introduced by PR #35300 which updated the typeahead module usage from ngx-bootstrap to ng-bootstrap's typeahead module. The regression was that the typeahead didn't open on click into the input field. Another regression was that the suggestions didn't overlap the modal anymore. Fixes: https://tracker.ceph.com/issues/46135 Signed-off-by: Stephan Müller --- .../silence-matcher-modal.component.html | 3 ++ .../silence-matcher-modal.component.spec.ts | 48 +++++++++++++++++++ .../silence-matcher-modal.component.ts | 42 +++++++++------- 3 files changed, 76 insertions(+), 17 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.html index 853b24520d20d..c41e700431991 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.html @@ -38,6 +38,9 @@ i18n>Value
{ }); component.onSubmit(); }); + + describe('typeahead', () => { + let equality: { [key: string]: boolean }; + let expectations: { [key: string]: string[] }; + + const search = (s: string) => { + Object.keys(expectations).forEach((key) => { + formH.setValue('name', key); + component.search(of(s)).subscribe((result) => { + // Expect won't fail the test inside subscribe + equality[key] = _.isEqual(result, expectations[key]); + }); + expect(equality[key]).toBeTruthy(); + }); + }; + + beforeEach(() => { + equality = { + alertname: false, + instance: false, + job: false, + severity: false + }; + expectations = { + alertname: ['alert0', 'alert1'], + instance: ['someInstance'], + job: ['someJob'], + severity: ['someSeverity'] + }; + }); + + it('should show all values on name switch', () => { + search(''); + }); + + it('should search for "some"', () => { + expectations['alertname'] = []; + search('some'); + }); + + it('should search for "er"', () => { + expectations['instance'] = []; + expectations['job'] = []; + search('er'); + }); + }); }); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.ts index cef04ef55595e..e0afaa805bd55 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-matcher-modal/silence-matcher-modal.component.ts @@ -1,10 +1,10 @@ -import { Component, EventEmitter, Output } from '@angular/core'; +import { Component, EventEmitter, Output, ViewChild } from '@angular/core'; import { FormControl, Validators } from '@angular/forms'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbActiveModal, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; import * as _ from 'lodash'; -import { Observable } from 'rxjs'; -import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; +import { merge, Observable, Subject } from 'rxjs'; +import { debounceTime, distinctUntilChanged, filter, map } from 'rxjs/operators'; import { CdFormBuilder } from '../../../../shared/forms/cd-form-builder'; import { CdFormGroup } from '../../../../shared/forms/cd-form-group'; @@ -21,6 +21,8 @@ import { PrometheusSilenceMatcherService } from '../../../../shared/services/pro styleUrls: ['./silence-matcher-modal.component.scss'] }) export class SilenceMatcherModalComponent { + @ViewChild(NgbTypeahead, { static: true }) + typeahead: NgbTypeahead; @Output() submitAction = new EventEmitter(); @@ -31,6 +33,24 @@ export class SilenceMatcherModalComponent { possibleValues: string[] = []; matcherMatch: AlertmanagerSilenceMatcherMatch = undefined; + // For typeahead usage + valueClick = new Subject(); + valueFocus = new Subject(); + search = (text$: Observable) => { + return merge( + text$.pipe(debounceTime(200), distinctUntilChanged()), + this.valueFocus, + this.valueClick.pipe(filter(() => !this.typeahead.isPopupOpen())) + ).pipe( + map((term) => + (term === '' + ? this.possibleValues + : this.possibleValues.filter((v) => v.toLowerCase().indexOf(term.toLowerCase()) > -1) + ).slice(0, 10) + ) + ); + }; + constructor( private formBuilder: CdFormBuilder, private silenceMatcher: PrometheusSilenceMatcherService, @@ -43,7 +63,7 @@ export class SilenceMatcherModalComponent { private createForm() { this.form = this.formBuilder.group({ name: [null, [Validators.required]], - value: [{ value: null, disabled: true }, [Validators.required]], + value: [{ value: '', disabled: true }, [Validators.required]], isRegex: new FormControl(false) }); } @@ -78,16 +98,4 @@ export class SilenceMatcherModalComponent { this.submitAction.emit(this.form.value); this.activeModal.close(); } - - search = (text$: Observable) => { - return text$.pipe( - debounceTime(200), - distinctUntilChanged(), - map((term) => - this.possibleValues - .filter((v) => v.toLowerCase().indexOf(term.toLowerCase()) > -1) - .slice(0, 10) - ) - ); - }; } -- 2.39.5