Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
#29 Get Technologies
  • Loading branch information
jorgecod committed Apr 16, 2020
commit 1c5733976d9050edcffedeb32c52d718dcef4dbd
3 changes: 2 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { HomeComponent } from './modules/home/home.component';
import { LoginComponent } from './modules/login/login.component';
import { ActivityEffects } from './modules/activities-management/store/activity-management.effects';
import { ProjectEffects } from './modules/project-management/store/project.effects';
import { TechnologyEffects } from './modules/shared/store/technology.effects';
import { reducers, metaReducers } from './reducers';
import { environment } from '../environments/environment';
import { CustomerComponent } from './modules/customer-managment/pages/customer.component';
Expand Down Expand Up @@ -99,7 +100,7 @@ import { CreateProjectTypeComponent } from './modules/customer-managment/compone
maxAge: 15, // Retains last 15 states
})
: [],
EffectsModule.forRoot([ProjectEffects, ActivityEffects]),
EffectsModule.forRoot([ProjectEffects, ActivityEffects, TechnologyEffects]),
],
providers: [],
bootstrap: [AppComponent],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,41 @@
aria-describedby="inputGroup-sizing-sm"
/>
</div>

<div class="input-group input-group-sm mb-3">
<div class="input-group-prepend">
<span class="input-group-text span-width" id="inputGroup-sizing-sm">Technology</span>
</div>
<input
formControlName="technology"
(keypress)="getTechnologies($event.target.value)"
type="text"
class="form-control"
aria-label="Small"
aria-describedby="inputGroup-sizing-sm"
/>
</div>

<app-search-project (changeFilterProject)="filterTechnology = $event"></app-search-project>
<div *ngIf="isLoading">LOADING...</div>
<div *ngIf="technologies" class="d-flex flex-column technology-content">
<div
*ngFor="let technology of technologies.items | filterProject: filterTechnology"
(click)="setTechnology(technology.name)"
class="technology-list"
[ngClass]="{ active: selectedTechnology.includes(technology.name) }"
>
{{ technology.name }}
</div>
</div>
<div class="tags-content d-flex flex-wrap">
<span
*ngFor="let technology of selectedTechnology; let tagIndex = index"
(click)="removeTag(tagIndex)"
class="tag"
>{{ technology }}</span
>
</div>

<div class="form-group">
<label for="NotesTextarea">Notes</label>
<textarea formControlName="comments" class="form-control" id="NotesTextarea" rows="3"></textarea>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
@import '../../../../../styles/colors.scss';

@mixin tagTechnology() {
background-color: $modal-button-secondary;
color: #ffffff;
cursor: pointer;

&:hover {
opacity: 0.8;
}
}

.span-width {
width: 6rem;
background-image: $background-pantone;
Expand All @@ -18,3 +29,52 @@
background-color: $modal-button-secondary;
color: white;
}

.technology-content {
margin-bottom: 2rem;
max-height: 7.5rem;
overflow-y: auto;

.technology-list {
background-color: #efefef;
cursor: pointer;
font-size: 0.8rem;
margin-bottom: 0.1rem;
padding: 0.2rem 0.5rem;

&:hover {
opacity: 0.7;
}
}

.active {
@include tagTechnology();
}
}

.tags-content {
margin-bottom: 2rem;

span {
@include tagTechnology();

border-radius: 0.2rem;
box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.75);
font-size: 0.8rem;
padding: 0 1rem 0.2rem 1.5rem;
position: relative;
margin: 0 0.5rem 0.5rem 0;

&::before {
background: #fff;
border-radius: 1rem;
box-shadow: inset 0 1px rgba(0, 0, 0, 0.6);
content: '';
height: 0.4rem;
left: 0.6rem;
position: absolute;
width: 0.4rem;
top: 0.5rem;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,46 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { provideMockStore, MockStore } from '@ngrx/store/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { TechnologyState } from '../../store/technology.reducers';
import { allTechnologies } from '../../store/technology.selectors';
import { DetailsFieldsComponent } from './details-fields.component';
import { FilterProjectPipe } from '../../../shared/pipes';
import * as actions from '../../store/technology.actions';

describe('DetailsFieldsComponent', () => {
let component: DetailsFieldsComponent;
let fixture: ComponentFixture<DetailsFieldsComponent>;
let store: MockStore<TechnologyState>;
let mockTechnologySelector;

const state = {
technologyList: { items: [{ name: 'java' }] },
isLoading: false,
};

const initialData = {
project: '',
activity: '',
ticket: '',
technology: '',
comments: ''
comments: '',
};

const newData = {
project: 'Ernst&Young',
activity: 'development',
ticket: 'WA-15',
technology: 'Angular',
comments: 'No notes'
comments: 'No notes',
};

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DetailsFieldsComponent],
imports: [
FormsModule,
ReactiveFormsModule
],
declarations: [DetailsFieldsComponent, FilterProjectPipe],
providers: [provideMockStore({ initialState: state })],
imports: [FormsModule, ReactiveFormsModule],
}).compileComponents();
store = TestBed.inject(MockStore);
mockTechnologySelector = store.overrideSelector(allTechnologies, state);
}));

beforeEach(() => {
Expand Down Expand Up @@ -59,4 +70,37 @@ describe('DetailsFieldsComponent', () => {
component.ngOnChanges();
expect(component.entryForm.value).toEqual(newData);
});

it('should dispatch LoadTechnology action #getTechnologies', () => {
const value = 'java';
spyOn(store, 'dispatch');
component.getTechnologies(value);

expect(store.dispatch).toHaveBeenCalledWith(new actions.LoadTechnology(value));
});

it('should add a new tag #setTechnology', () => {
const name = 'ngrx';
component.selectedTechnology = ['java', 'javascript'];
component.selectedTechnology.indexOf(name);
component.selectedTechnology.length = 2;
component.setTechnology(name);
expect(component.selectedTechnology.length).toBe(3);
});

it('should call the removeTag function #setTechnology', () => {
const name = 'java';
component.selectedTechnology = ['java', 'javascript'];
const index = component.selectedTechnology.indexOf(name);
spyOn(component, 'removeTag');
component.setTechnology(name);
expect(component.removeTag).toHaveBeenCalledWith(index);
});

it('should call the removeTag() function #removeTag', () => {
const index = 1;
component.selectedTechnology = ['java', 'angular'];
component.removeTag(index);
expect(component.selectedTechnology.length).toBe(1);
});
});
Original file line number Diff line number Diff line change
@@ -1,48 +1,78 @@
import {
Component,
OnChanges,
Input,
Output,
EventEmitter,
ViewChild,
ElementRef
} from '@angular/core';
import { Component, OnChanges, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { Store, select } from '@ngrx/store';
import * as actions from '../../store/technology.actions';

import { allTechnologies } from '../../store/technology.selectors';
import { Technology } from '../../models';

@Component({
selector: 'app-details-fields',
templateUrl: './details-fields.component.html',
styleUrls: ['./details-fields.component.scss']
styleUrls: ['./details-fields.component.scss'],
})
export class DetailsFieldsComponent implements OnChanges {
export class DetailsFieldsComponent implements OnChanges, OnInit {
@Input() entryToEdit;
@Input() formType: string;
@Output() saveEntry = new EventEmitter();
@ViewChild('closeModal') closeModal: ElementRef;
entryForm: FormGroup;
technologies: Technology;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technologies or technology. it looks like technology since the type is not an array or any kind of collection.

filterTechnology = '';
selectedTechnology: string[] = [];
isLoading = false;

constructor(private formBuilder: FormBuilder) {
constructor(private formBuilder: FormBuilder, private store: Store<Technology>) {
this.entryForm = this.formBuilder.group({
project: '',
activity: '',
ticket: '',
technology: '',
comments: ''
comments: '',
});
}

ngOnInit(): void {
const technologies$ = this.store.pipe(select(allTechnologies));
technologies$.subscribe((response) => {
this.isLoading = response.isLoading;
this.technologies = response.technologyList;
});
}

ngOnChanges(): void {
if (this.entryToEdit) {
this.selectedTechnology = this.entryToEdit.technology;

this.entryForm.setValue({
project: this.entryToEdit.project,
activity: this.entryToEdit.activity,
ticket: this.entryToEdit.ticket,
technology: this.entryToEdit.technology,
comments: this.entryToEdit.comments
comments: this.entryToEdit.comments,
});
}
}

getTechnologies(value) {
if (value.length >= 2) {
this.store.dispatch(new actions.LoadTechnology(value));
}
}

setTechnology(name: string) {
const index = this.selectedTechnology.indexOf(name);
if (index > -1) {
this.removeTag(index);
} else {
if (this.selectedTechnology.length < 10) {
this.selectedTechnology = [...this.selectedTechnology, name];
}
}
}

removeTag(index) {
this.selectedTechnology.splice(index, 1);
}

onSubmit() {
this.saveEntry.emit(this.entryForm.value);
this.closeModal.nativeElement.click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('ModalComponent', () => {
startDate: '2020-02-05T15:36:15.887Z',
endDate: '2020-02-05T18:36:15.887Z',
activity: 'development',
technology: 'Angular, TypeScript',
technology: ['Angular', 'TypeScript'],
};

spyOn(component.removeList, 'emit');
Expand Down
2 changes: 1 addition & 1 deletion src/app/modules/shared/models/entry.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export interface Entry {
startDate: string;
endDate: string;
activity: string;
technology: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technologies

technology: string[];
comments?: string;
ticket?: string;
}
1 change: 1 addition & 0 deletions src/app/modules/shared/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './activity.model';
export * from './entry.model';
export * from './project.model';
export * from './technology.model';
3 changes: 3 additions & 0 deletions src/app/modules/shared/models/technology.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface Technology {
items: Array<any>;
}
Empty file.
45 changes: 45 additions & 0 deletions src/app/modules/shared/services/technology.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { TestBed, inject } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { Technology } from '../../shared/models';
import { TechnologyService } from './technology.service';
import { KEY } from '../../../../environments/environment';

describe('TechnologyService', () => {
let service: TechnologyService;
let httpMock: HttpTestingController;

const technologyList: Technology = { items: [{ name: 'java' }, { name: 'javascript' }] };

beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
});
service = TestBed.inject(TechnologyService);
httpMock = TestBed.inject(HttpTestingController);
});

afterEach(() => {
httpMock.verify();
});

it('should create', inject(
[HttpClientTestingModule, TechnologyService],
(httpClient: HttpClientTestingModule, apiService: TechnologyService) => {
expect(apiService).toBeTruthy();
expect(httpClient).toBeTruthy();
}
));

it('technologies are read using GET from url', () => {
const technologyFoundSize = technologyList.items.length;
const technologyName = 'java';
service.getTechnologies(technologyName).subscribe((technologyInResponse) => {
expect(technologyInResponse.items.length).toBe(technologyFoundSize);
});
const getTechnologiesRequest = httpMock.expectOne(
`${service.baseUrl}&inname=${technologyName}&site=stackoverflow&key=${KEY}`
);
expect(getTechnologiesRequest.request.method).toBe('GET');
getTechnologiesRequest.flush(technologyList);
});
});
Loading