Skip to content

Commit

Permalink
Initial support for search filter on executions list page
Browse files Browse the repository at this point in the history
  • Loading branch information
GuyShaanan committed Jul 27, 2017
1 parent 535f32a commit a49c73c
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 45 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"scripts": {
"ng": "ng",
"start": "ng serve --host 0.0.0.0 --port 4200 --publicHost http://0.0.0.0:4200 --disableHostCheck --proxy-config proxy.conf.json",
"build": "ng build --aot --prod",
"build": "ng build --aot --prod --output-hashing=bundles",
"test": "ng test",
"test-ci": "ng test --singleRun",
"test-coverage": "ng test --singleRun --code-coverage --reporters=coverage-istanbul",
Expand All @@ -24,7 +24,7 @@
"@angular/platform-browser": "4.3.1",
"@angular/platform-browser-dynamic": "4.3.1",
"@angular/router": "4.3.1",
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.28",
"@ng-bootstrap/ng-bootstrap": "1.0.0-alpha.29",
"angular-split": "^0.2.0",
"animate.css": "^3.5.2",
"bootstrap": "4.0.0-alpha.6",
Expand All @@ -39,17 +39,17 @@
"moment": "^2.17.1",
"ngx-clipboard": "^8.0.2",
"open-sans-fontface": "^1.4.0",
"rxjs": "^5.1.0",
"rxjs": "^5.4.0",
"tether": "^1.4.0",
"zone.js": "^0.8.4"
"zone.js": "^0.8.14"
},
"devDependencies": {
"@angular/cli": "1.2.3",
"@angular/cli": "1.2.5",
"@angular/compiler-cli": "4.3.1",
"@angular/language-service": "4.3.1",
"@types/codemirror": "^0.0.38",
"@types/dagre": "^0.7.34",
"@types/jasmine": "2.5.45",
"@types/jasmine": "~2.5.53",
"@types/jquery": "^3.2.6",
"@types/js-yaml": "^3.5.31",
"@types/node": "~6.0.60",
Expand Down
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {ExecutionsModule} from "./executions/executions.module";
import {MistralService} from "./engines/mistral/mistral.service";
import {AboutComponent} from "./about/about.component";
import {FormsModule} from "@angular/forms";

@NgModule({
declarations: [
Expand All @@ -24,6 +25,7 @@ import {AboutComponent} from "./about/about.component";
BrowserAnimationsModule,
HttpClientModule,
ExecutionsModule,
FormsModule,
],
providers: [
MistralService
Expand Down
22 changes: 12 additions & 10 deletions src/app/executions/executions-list/executions-list.component.html
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
<div class="bg-white d-flex search-header header-shadow">
<div class="container d-flex flex-column justify-content-around">
<div class="pt-2">
<p><input type="text" class="form-control" placeholder="Search workflows"></p>
<p><strong>Showing {{executions.length}} Executions</strong></p>
<p><input type="text" class="form-control" placeholder="Search workflows" [(ngModel)]="search"></p>
<!--<p><strong>Showing {{executions.length}} Executions</strong></p>-->
</div>

<div class="row pb-1 m-0 text-muted">
<div class="col-sm-1 col-3">State</div>
<div class="col-sm-8 col-4">Workflow</div>
<div class="col-sm-3 col-5">Created At</div>
<div class="col-sm-3 col-5"><i class="fa fa-caret-down"></i> Created At</div>
</div>

</div>
</div>

<div class="container search-results">
<div class="" id="executions-list">
<div class="row py-3 m-0 execution-list-item pointer" *ngFor="let execution of executions; trackBy: execTrackBy" [routerLink]="[execution.id]">
<div class="col-sm-1 col-3">
<span class="badge font-weight-normal" [ngClass]="execution.state">{{execution.state}}</span>
<div id="executions-list">
<ng-container *ngFor="let execution of executions | search : search ; trackBy: execTrackBy">
<div [routerLink]="[execution.id]" class="row py-3 m-0 execution-list-item pointer">
<div class="col-sm-1 col-3">
<span class="badge font-weight-normal" [ngClass]="execution.state">{{execution.state}}</span>
</div>
<div class="col-sm-8 col-4 word-break-all">{{execution.workflow_name}}</div>
<div class="col-sm-3 col-5">{{execution.created_at}}</div>
</div>
<div class="col-sm-8 col-4 word-break-all">{{execution.workflow_name}}</div>
<div class="col-sm-3 col-5">{{execution.created_at}}</div>
</div>
</ng-container>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {Execution} from "../../shared/models/execution";
})
export class ExecutionsListComponent implements OnInit {
executions: Execution[] = [];
search: string;

constructor(private service: MistralService) {}

Expand Down
89 changes: 89 additions & 0 deletions src/app/shared/filters/search.pipe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (C) 2017 Nokia

import {SearchPipe} from './search.pipe';

describe('SearchPipe', () => {
let pipe: SearchPipe;
const input = [{animal: "Cat", age: 6}, {animal: "Dog", age: 12}];

beforeEach(() => {
pipe = new SearchPipe();
});

it('return the whole array when search value is empty', () => {
expect(pipe.transform(input, "")).toEqual(input);
});

describe('Test empty arrays', () => {
it('return empty array on null', () => {
expect(pipe.transform(null, "")).toEqual([]);
});

it('return empty array on undefined', () => {
expect(pipe.transform(undefined, "")).toEqual([]);
});

it('return empty array on empty array', () => {
expect(pipe.transform([], "")).toEqual([]);
});
});

describe('Array filter with field name', () => {
it('should find an item', () => {
const result = pipe.transform(input, "cat", "animal");
expect(result.length).toEqual(1);
expect(result[0].age).toEqual(6);
});

it("shouldn't find an item", () => {
const result = pipe.transform(input, "jaguar", "animal");
expect(result.length).toEqual(0);
});
});

describe('Array filter on any field', () => {
it('should find an item', () => {
const result = pipe.transform(input, "12");
expect(result.length).toEqual(1);
expect(result[0].animal).toEqual("Dog");
});

it("Shouldn't find any items", () => {
const result = pipe.transform(input, "14");
expect(result.length).toEqual(0);
});
});

describe('Ignore case', () => {
it('should find an item', () => {
const result = pipe.transform(input, "cat");
expect(result.length).toEqual(1);
expect(result[0].animal).toEqual("Cat");
});

it("shouldn't find an item", () => {
const result = pipe.transform(input, "jaguar");
expect(result.length).toEqual(0);
});
});

describe('Partial input', () => {
it('should find 2 items', () => {
const input2 = [{name: "ABCD"}, {name: "FABCE"}, {name: "DCBA"}];
const result = pipe.transform(input2, "ab");
expect(result.length).toEqual(2);
});

it("shouldn't find any items", () => {
const input2 = [{name: "ABCD"}, {name: "FABCE"}, {name: "DCBA"}];
const result = pipe.transform(input2, "abe");
expect(result.length).toEqual(0);
});
});

describe("Don't fail on null values of fields", () => {
it("Shouldn't fail on null value", () => {
expect(() => pipe.transform([{name: null}], "val")).not.toThrow();
});
});
});
33 changes: 33 additions & 0 deletions src/app/shared/filters/search.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (C) 2017 Nokia

import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
name: 'search'
})
export class SearchPipe implements PipeTransform {

private static fieldMatch(item: any, fieldName: string, searchValue: string): boolean {
return item[fieldName] != null &&
item[fieldName].toString().toLocaleLowerCase().includes(searchValue);
}

private static anywhere(item: any, searchValue: string): boolean {
return Object.keys(item).some(key => SearchPipe.fieldMatch(item, key, searchValue));
}

private static matches(item: any, searchValue: string, fieldName?: string): boolean {
return fieldName ? SearchPipe.fieldMatch(item, fieldName, searchValue) : SearchPipe.anywhere(item, searchValue);
}

transform(items: any[], searchValue: string, fieldName?: string): any {
if (!items) {
return [];
}
if (!searchValue) {
return items;
}
return items.filter(it => SearchPipe.matches(it, searchValue.toLocaleLowerCase(), fieldName));
}

}
5 changes: 4 additions & 1 deletion src/app/shared/shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {AngularSplitModule} from "angular-split";
import {CodeMirrorModule} from "./components/codemirror/codemirror.module";
import {ClipboardModule} from "ngx-clipboard/dist";
import {CopyableComponent} from "./components/copyable.component";
import {SearchPipe} from './filters/search.pipe';

@NgModule({
imports: [
Expand All @@ -20,6 +21,7 @@ import {CopyableComponent} from "./components/copyable.component";
],
declarations: [
CopyableComponent,
SearchPipe,
],
exports: [
CommonModule,
Expand All @@ -28,7 +30,8 @@ import {CopyableComponent} from "./components/copyable.component";
AngularSplitModule,
CodeMirrorModule,
ClipboardModule,
CopyableComponent
CopyableComponent,
SearchPipe,
]
})
export class SharedModule {
Expand Down
1 change: 0 additions & 1 deletion src/app/shared/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ describe('Test utils', () => {
it('should convert all values to strings', () => {
const params = toUrlParams({id: 30});
expect(params.get("id")).toEqual("30");
expect(params.get("id")).not.toEqual(30);
});
});

Expand Down
Loading

0 comments on commit a49c73c

Please sign in to comment.