Skip to content

Commit 6babc3e

Browse files
committed
feat: TT-18 Allow users navigate with keyboard arrows between the technologies list
1 parent 9871b4c commit 6babc3e

File tree

12 files changed

+131
-152
lines changed

12 files changed

+131
-152
lines changed

angular.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"src/styles.scss",
3232
"node_modules/ngx-toastr/toastr.css",
3333
"./node_modules/ngx-ui-switch/ui-switch.component.css",
34-
"node_modules/datatables.net-buttons-dt/css/buttons.dataTables.css"
34+
"node_modules/datatables.net-buttons-dt/css/buttons.dataTables.css",
35+
"node_modules/@ng-select/ng-select/themes/default.theme.css"
3536
],
3637
"scripts": [
3738
"node_modules/jquery/dist/jquery.js",

package-lock.json

Lines changed: 30 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@angular/router": "10.2.2",
2828
"@azure/app-configuration": "1.1.0",
2929
"@azure/identity": "1.1.0",
30+
"@ng-select/ng-select": "7.2.0",
3031
"@ngrx/effects": "10.0.1",
3132
"@ngrx/store": "10.0.1",
3233
"@ngrx/store-devtools": "10.0.1",

src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import { TechnologyReportTableComponent } from './modules/technology-report/comp
8585
import { TechnologyReportComponent } from './modules/technology-report/pages/technology-report.component';
8686
import { CalendarComponent } from './modules/time-entries/components/calendar/calendar.component';
8787
import { DropdownComponent } from './modules/shared/components/dropdown/dropdown.component';
88+
import { NgSelectModule } from '@ng-select/ng-select';
8889

8990
const maskConfig: Partial<IConfig> = {
9091
validation: false,
@@ -181,6 +182,7 @@ const maskConfig: Partial<IConfig> = {
181182
provide: DateAdapter,
182183
useFactory: adapterFactory,
183184
}),
185+
NgSelectModule
184186
],
185187
providers: [
186188
{

src/app/modules/shared/components/details-fields/details-fields.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@
157157
</div>
158158

159159
<app-technologies
160-
(technologyAdded)="onTechnologiesUpdated($event)"
161-
(technologyRemoved)="onTechnologiesUpdated($event)"
160+
(technologyUpdated)="onTechnologiesUpdated($event)"
162161
[selectedTechnologies]="selectedTechnologies"
162+
[isDisabled]="isTechnologiesDisabled"
163163
#technologies
164164
>
165165
</app-technologies>

src/app/modules/shared/components/details-fields/details-fields.component.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
5151
activities$: Observable<Activity[]>;
5252
goingToWorkOnThis = false;
5353
shouldRestartEntry = false;
54+
isTechnologiesDisabled = true;
5455
projectKeyForLocalStorage = PROJECTS_KEY_FOR_LOCAL_STORAGE;
5556

5657
constructor(
@@ -121,13 +122,15 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
121122
}
122123

123124
onClearedComponent(event) {
125+
this.isTechnologiesDisabled = true;
124126
this.entryForm.patchValue({
125127
project_id: '',
126128
project_name: '',
127129
});
128130
}
129131

130132
onSelectedProject(item) {
133+
this.isTechnologiesDisabled = false;
131134
this.projectSelected.emit({ projectId: item.id });
132135
this.entryForm.patchValue({
133136
project_id: item.id,
@@ -155,6 +158,7 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
155158
this.shouldRestartEntry = false;
156159

157160
if (this.entryToEdit) {
161+
this.isTechnologiesDisabled = false;
158162
this.selectedTechnologies = this.entryToEdit.technologies;
159163
const projectFound = this.listProjects.find((project) => project.id === this.entryToEdit.project_id);
160164
this.entryForm.setValue({
@@ -185,13 +189,13 @@ export class DetailsFieldsComponent implements OnChanges, OnInit {
185189
);
186190
} else {
187191
this.cleanForm();
192+
this.isTechnologiesDisabled = true;
188193
this.activities$ = this.selectActiveActivities();
189194
}
190195
}
191196

192197
cleanForm(skipProject: boolean = false): void {
193198
this.selectedTechnologies = [];
194-
this.technologies.query = '';
195199
const projectNameField = this.project_name.value;
196200
const projectName = get(projectNameField, 'search_field', projectNameField);
197201
this.entryForm.reset({
Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,24 @@
11
<div class="form-group">
22
<label>Technology:</label>
3-
<input
4-
autocomplete="off"
5-
(keyup)="queryTechnologies($event)"
6-
id="technologies"
7-
type="text"
8-
class="form-control"
9-
[(ngModel)]="query"
10-
/>
113

12-
<div *ngIf="isLoading">
13-
<img src="assets/img/spinner.gif" />
14-
</div>
4+
<ng-select
5+
bindLabel="name"
6+
bindValue="name"
7+
ngDefaultControl
8+
[items]="technologies"
9+
[loading]="isLoading"
10+
[disabled]='isDisabled'
11+
[searchable]="ALLOW_SEARCH"
12+
[multiple]="ALLOW_SELECT_MULTIPLE"
13+
[placeholder]="TECHNOLOGIES_PLACEHOLDER"
14+
[notFoundText]="NO_RESULTS_MESSAGE"
15+
[maxSelectedItems]='MAX_NUM_TECHNOLOGIES'
16+
[minTermLength]="MIN_SEARCH_TERM_LENGTH"
17+
[typeToSearchText]="TYPE_TO_SEARCH_TEXT"
18+
[typeahead]="technologiesInput$"
19+
[(ngModel)]="selectedTechnologies"
20+
(change)="updateTechnologies()">
1521

16-
<div #technologiesDropdown *ngIf="technology && showList" class="form-group technologies-dropdown-container">
17-
<div
18-
*ngFor="let item of technology.items"
19-
(click)="addTechnology(item.name)"
20-
class="technologies-dropdown-item">
21-
{{ item.name }}
22-
</div>
23-
</div>
24-
</div>
25-
<div class="form-group">
26-
<div class="form-group selected-technologies-container" *ngIf="selectedTechnologies?.length">
27-
<div *ngFor="let technology of selectedTechnologies; let tagIndex = index" class="selected-technology">
28-
<i class="fas fa-times text-white" (click)="removeTechnology(tagIndex)"></i>
29-
<span style="padding-left: 0.5rem">{{ technology }}</span>
30-
</div>
31-
</div>
32-
<div class="form-group" *ngIf="selectedTechnologies === null || selectedTechnologies.length === 0">
33-
<!-- empty-space -->
34-
</div>
22+
</ng-select>
3523

3624
</div>
37-
38-
39-

src/app/modules/shared/components/technologies/technologies.component.spec.ts

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,49 +40,34 @@ describe('Technologies component', () => {
4040
expect(component).toBeTruthy();
4141
});
4242

43-
it('when a new technology is added, it should be added to the selectedTechnologies list', () => {
44-
const name = 'ngrx';
45-
component.selectedTechnologies = ['java', 'javascript'];
46-
component.selectedTechnologies.indexOf(name);
47-
length = component.selectedTechnologies.length;
48-
component.addTechnology(name);
49-
expect(component.selectedTechnologies.length).toBe(3);
50-
});
43+
it('When technologies are updated, technolgyUpdated should emit an event with new Technologies', () => {
44+
const selectedTechnologies = ['java', 'angular'];
45+
const technologyUpdatedSpy = spyOn(component.technologyUpdated, 'emit');
46+
component.selectedTechnologies = selectedTechnologies;
5147

52-
it('when the max number of technologies is reached, then adding technologies is not allowed', () => {
53-
const name = 'ngrx';
54-
component.selectedTechnologies = [
55-
'java',
56-
'javascript',
57-
'angular',
58-
'angular-ui',
59-
'typescript',
60-
'scss',
61-
'bootstrap',
62-
'jasmine',
63-
'karma',
64-
'github',
65-
];
66-
length = component.selectedTechnologies.length;
67-
component.addTechnology(name);
68-
expect(component.selectedTechnologies.length).toBe(10);
69-
});
48+
component.updateTechnologies();
7049

71-
it('when a technology is removed, then it should be removed from the technologies list', () => {
72-
const index = 1;
73-
component.selectedTechnologies = ['java', 'angular'];
74-
component.removeTechnology(index);
75-
expect(component.selectedTechnologies.length).toBe(1);
50+
expect(technologyUpdatedSpy).toHaveBeenCalled();
51+
expect(technologyUpdatedSpy).toHaveBeenCalledWith(selectedTechnologies);
52+
expect(component.technologies).toEqual([]);
7653
});
7754

7855
it('when querying technologies, then a FindTechnology action should be dispatched', () => {
7956
const query = 'react';
80-
const target = {value: query};
81-
const event = new InputEvent('input');
82-
spyOnProperty(event, 'target').and.returnValue(target);
8357
spyOn(store, 'dispatch');
84-
component.queryTechnologies(event);
58+
component.searchTechnologies(query);
8559

8660
expect(store.dispatch).toHaveBeenCalledWith(new actions.FindTechnology(query));
8761
});
62+
63+
it('calls unsubscribe on ngDestroy', () => {
64+
65+
const technologyInputSpy = spyOn(component.technologiesInputSubscription, 'unsubscribe');
66+
const technologiesSpy = spyOn(component.technologiesSubscription, 'unsubscribe');
67+
68+
component.ngOnDestroy();
69+
70+
expect(technologyInputSpy).toHaveBeenCalled();
71+
expect(technologiesSpy).toHaveBeenCalled();
72+
});
8873
});

0 commit comments

Comments
 (0)