File

src/app/graphs/label-search/label-search.component.ts

Description

This component is responsible for the display of the search bar above the graph. It allows for filtering issues by multiple labels and text fragments.

Implements

OnInit

Metadata

selector app-label-search
styleUrls ./label-search.component.scss
templateUrl ./label-search.component.html

Index

Properties
Methods

Constructor

constructor(labelStore: LabelStoreService, ss: StateService)
Parameters :
Name Type Optional
labelStore LabelStoreService No
ss StateService No

Methods

emitSelectedLabels
emitSelectedLabels()

Emit value representing label and text fragments in the search bar via this.filterSelection$

Returns : void
Private loadLabels
loadLabels()

Load all labels from backend that match the currently typed in ng-select element

Returns : void
trackByFn
trackByFn(item: FilterLabel)
Parameters :
Name Type Optional
item FilterLabel No
Returns : string

Properties

Public filterSelection$
Default value : new BehaviorSubject<FilterSelection>({ labels: [], texts: [] })
labels$
Type : Observable<FilterLabel[]>
labelsInput$
Default value : new Subject<string>()
labelsLoading
Default value : false
selectedLabels
Type : FilterElement[]
Default value : []
import {Component, OnInit} from '@angular/core';
import {concat, of, Subject, Observable, BehaviorSubject} from 'rxjs';
import {catchError, distinctUntilChanged, switchMap, tap} from 'rxjs/operators';
import {FilterLabel, isFilterLabel, LabelStoreService} from '../../data/label/label-store.service';
import {StateService} from '../../state.service';

/**
 * This component is responsible for the display of the search bar above the graph.
 * It allows for filtering issues by multiple labels and text fragments.
 */
@Component({
  selector: 'app-label-search',
  templateUrl: './label-search.component.html',
  styleUrls: ['./label-search.component.scss']
})
export class LabelSearchComponent implements OnInit {
  public filterSelection$ = new BehaviorSubject<FilterSelection>({
    labels: [],
    texts: []
  });

  labels$: Observable<FilterLabel[]>;
  labelsLoading = false;
  labelsInput$ = new Subject<string>();
  selectedLabels: FilterElement[] = [];

  constructor(private labelStore: LabelStoreService, private ss: StateService) {}

  ngOnInit() {
    this.loadLabels();
  }

  trackByFn(item: FilterLabel): string {
    return item.id;
  }

  /**
   * Emit value representing label and text fragments in the search bar via this.filterSelection$
   */
  emitSelectedLabels(): void {
    const selection: FilterSelection = {texts: [], labels: []};
    // find out which elements in search bar correspond to an existing label on the backend and which to a text fragment
    selection.texts = this.selectedLabels.filter((item) => !isFilterLabel(item)).map((item) => item.name);
    selection.labels = this.selectedLabels.filter((label) => isFilterLabel(label)) as FilterLabel[];
    this.filterSelection$.next(selection);
  }

  /**
   * Load all labels from backend that match the currently typed in ng-select element
   */
  private loadLabels() {
    this.labels$ = concat(
      of([]), // default items
      this.labelsInput$.pipe(
        distinctUntilChanged(),
        tap(() => (this.labelsLoading = true)),
        switchMap((term) =>
          this.labelStore.getMatchingLabels(this.ss.state.project.node.id, term).pipe(
            catchError(() => of([])), // empty list on error
            tap(() => (this.labelsLoading = false))
          )
        )
      )
    );
  }
}

/**
 * The bar can contain elements standing for labels and elements for text fragments.
 */
type FilterElement = TextFragment | FilterLabel;

interface TextFragment {
  name: string;
}

export interface FilterSelection {
  texts: string[];
  labels: FilterLabel[];
}
<ng-select
  class="label-select"
  [items]="labels$ | async"
  bindLabel="name"
  [addTag]="true"
  [multiple]="true"
  [hideSelected]="true"
  [trackByFn]="trackByFn"
  [loading]="labelsLoading"
  typeToSearchText="Search by label or text"
  addTagText="Search text in issue title & body"
  [typeahead]="labelsInput$"
  [(ngModel)]="selectedLabels"
  ngDefaultControl
  (change)="emitSelectedLabels()"
>
</ng-select>

./label-search.component.scss

.ng-select.label-select ::ng-deep .ng-select-container {
  min-height: 0;
}

.ng-select.label-select ::ng-deep .ng-value-container {
  border-top: 7px solid transparent;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""