From 93c1d715bced0f482aaa02e42d90e75367d2dd54 Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Fri, 4 Oct 2024 14:02:15 +0200 Subject: [PATCH 1/9] Initial setup of search --- .../course-faq/course-faq.component.html | 5 +-- .../course-faq/course-faq.component.ts | 11 +++++-- .../app/shared/search/search.component.html | 19 ++++++++++++ .../app/shared/search/search.component.scss | 4 +++ .../app/shared/search/search.component.ts | 31 +++++++++++++++++++ 5 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 src/main/webapp/app/shared/search/search.component.html create mode 100644 src/main/webapp/app/shared/search/search.component.scss create mode 100644 src/main/webapp/app/shared/search/search.component.ts diff --git a/src/main/webapp/app/overview/course-faq/course-faq.component.html b/src/main/webapp/app/overview/course-faq/course-faq.component.html index c94960161ee4..c3d5f93c5c0d 100644 --- a/src/main/webapp/app/overview/course-faq/course-faq.component.html +++ b/src/main/webapp/app/overview/course-faq/course-faq.component.html @@ -1,6 +1,7 @@
-
-
+
+ +
+
+
+ diff --git a/src/main/webapp/app/shared/search/search.component.scss b/src/main/webapp/app/shared/search/search.component.scss new file mode 100644 index 000000000000..bbe25b7a4d50 --- /dev/null +++ b/src/main/webapp/app/shared/search/search.component.scss @@ -0,0 +1,4 @@ +.mn-icon-field { + margin-left: -3.5rem !important; + z-index: 10; +} diff --git a/src/main/webapp/app/shared/search/search.component.ts b/src/main/webapp/app/shared/search/search.component.ts new file mode 100644 index 000000000000..cea87dd70cb9 --- /dev/null +++ b/src/main/webapp/app/shared/search/search.component.ts @@ -0,0 +1,31 @@ +import { Component, output } from '@angular/core'; +import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { ArtemisSharedModule } from 'app/shared/shared.module'; +import { faMagnifyingGlass, faTimes } from '@fortawesome/free-solid-svg-icons'; + +@Component({ + selector: 'jhi-search', + templateUrl: './search.component.html', + styleUrls: ['./search.component.scss'], + standalone: true, + imports: [ReactiveFormsModule, ArtemisSharedModule], +}) +export class SearchComponent { + faMagnifyingGlass = faMagnifyingGlass; + faTimes = faTimes; + + searchValue: string; + newSearchEvent = output(); + + filterForm: FormGroup = new FormGroup({ + searchFilter: new FormControl(''), + }); + + setSearchValue(searchValue: string) { + this.searchValue = searchValue; + } + + applySearch(searchValue: string) { + this.newSearchEvent.emit(searchValue); + } +} From fb1ad44311515a1d5cfba7ba5a040872291678a6 Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Mon, 7 Oct 2024 09:05:33 +0200 Subject: [PATCH 2/9] Implementation of Search, Make SearchFilter standalone --- src/main/webapp/app/faq/faq.service.ts | 12 +++++++ .../course-faq/course-faq.component.html | 2 +- .../course-faq/course-faq.component.ts | 28 ++++++++++++----- .../search-filter/search-filter.component.ts | 3 ++ .../app/shared/search/search.component.html | 19 ------------ .../app/shared/search/search.component.scss | 4 --- .../app/shared/search/search.component.ts | 31 ------------------- src/main/webapp/app/shared/shared.module.ts | 3 -- .../app/shared/sidebar/sidebar.module.ts | 2 ++ .../course-faq/course-faq.component.spec.ts | 21 +++++++++++-- .../spec/service/faq.service.spec.ts | 9 ++++++ 11 files changed, 67 insertions(+), 67 deletions(-) delete mode 100644 src/main/webapp/app/shared/search/search.component.html delete mode 100644 src/main/webapp/app/shared/search/search.component.scss delete mode 100644 src/main/webapp/app/shared/search/search.component.ts diff --git a/src/main/webapp/app/faq/faq.service.ts b/src/main/webapp/app/faq/faq.service.ts index d0c80cf72e94..c98253e5223e 100644 --- a/src/main/webapp/app/faq/faq.service.ts +++ b/src/main/webapp/app/faq/faq.service.ts @@ -137,4 +137,16 @@ export class FaqService { return categories.some((category) => filteredCategory.has(category!)); } } + + hasSearchTokens(faq: Faq, searchTerm: string) { + if (searchTerm == '') { + return true; + } + const tokens = searchTerm.split(' '); + if (tokens) { + let faqText = faq.questionTitle + ' ' + faq.questionAnswer; + faqText = faqText.toLowerCase(); + return tokens.every((token) => faqText.includes(token.toLowerCase())); + } + } } diff --git a/src/main/webapp/app/overview/course-faq/course-faq.component.html b/src/main/webapp/app/overview/course-faq/course-faq.component.html index c3d5f93c5c0d..115efb80cc1c 100644 --- a/src/main/webapp/app/overview/course-faq/course-faq.component.html +++ b/src/main/webapp/app/overview/course-faq/course-faq.component.html @@ -1,6 +1,6 @@
- +
-
-
- diff --git a/src/main/webapp/app/shared/search/search.component.scss b/src/main/webapp/app/shared/search/search.component.scss deleted file mode 100644 index bbe25b7a4d50..000000000000 --- a/src/main/webapp/app/shared/search/search.component.scss +++ /dev/null @@ -1,4 +0,0 @@ -.mn-icon-field { - margin-left: -3.5rem !important; - z-index: 10; -} diff --git a/src/main/webapp/app/shared/search/search.component.ts b/src/main/webapp/app/shared/search/search.component.ts deleted file mode 100644 index cea87dd70cb9..000000000000 --- a/src/main/webapp/app/shared/search/search.component.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Component, output } from '@angular/core'; -import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { faMagnifyingGlass, faTimes } from '@fortawesome/free-solid-svg-icons'; - -@Component({ - selector: 'jhi-search', - templateUrl: './search.component.html', - styleUrls: ['./search.component.scss'], - standalone: true, - imports: [ReactiveFormsModule, ArtemisSharedModule], -}) -export class SearchComponent { - faMagnifyingGlass = faMagnifyingGlass; - faTimes = faTimes; - - searchValue: string; - newSearchEvent = output(); - - filterForm: FormGroup = new FormGroup({ - searchFilter: new FormControl(''), - }); - - setSearchValue(searchValue: string) { - this.searchValue = searchValue; - } - - applySearch(searchValue: string) { - this.newSearchEvent.emit(searchValue); - } -} diff --git a/src/main/webapp/app/shared/shared.module.ts b/src/main/webapp/app/shared/shared.module.ts index ed2bf4cc9dfd..9c0e71a1bc90 100644 --- a/src/main/webapp/app/shared/shared.module.ts +++ b/src/main/webapp/app/shared/shared.module.ts @@ -27,7 +27,6 @@ import { StickyPopoverDirective } from 'app/shared/sticky-popover/sticky-popover import { ConfirmEntityNameComponent } from 'app/shared/confirm-entity-name/confirm-entity-name.component'; import { DetailOverviewNavigationBarComponent } from 'app/shared/detail-overview-navigation-bar/detail-overview-navigation-bar.component'; import { ScienceDirective } from 'app/shared/science/science.directive'; -import { SearchFilterComponent } from './search-filter/search-filter.component'; @NgModule({ imports: [ArtemisSharedLibsModule, ArtemisSharedCommonModule, ArtemisSharedPipesModule, RouterModule], @@ -56,7 +55,6 @@ import { SearchFilterComponent } from './search-filter/search-filter.component'; AssessmentWarningComponent, StickyPopoverDirective, ScienceDirective, - SearchFilterComponent, ], exports: [ ArtemisSharedLibsModule, @@ -87,7 +85,6 @@ import { SearchFilterComponent } from './search-filter/search-filter.component'; CompetencySelectionComponent, StickyPopoverDirective, ScienceDirective, - SearchFilterComponent, ], }) export class ArtemisSharedModule {} diff --git a/src/main/webapp/app/shared/sidebar/sidebar.module.ts b/src/main/webapp/app/shared/sidebar/sidebar.module.ts index cc34473eb48f..fdb5efb40bff 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar.module.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar.module.ts @@ -16,6 +16,7 @@ import { SidebarCardDirective } from 'app/shared/sidebar/sidebar-card.directive' import { ConversationOptionsComponent } from 'app/shared/sidebar/conversation-options/conversation-options.component'; import { AccordionAddOptionsComponent } from 'app/shared/sidebar/accordion-add-options/accordion-add-options.component'; import { ArtemisExamSharedModule } from 'app/exam/shared/exam-shared.module'; +import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; @NgModule({ imports: [ @@ -28,6 +29,7 @@ import { ArtemisExamSharedModule } from 'app/exam/shared/exam-shared.module'; SubmissionResultStatusModule, SidebarCardDirective, ArtemisExamSharedModule, + SearchFilterComponent, ], declarations: [ SidebarAccordionComponent, diff --git a/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts b/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts index 6c5a21b56ddb..d7a9b7f40080 100644 --- a/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts @@ -22,8 +22,8 @@ import { FaqCategory } from 'app/entities/faq-category.model'; function createFaq(id: number, category: string, color: string): Faq { const faq = new Faq(); faq.id = id; - faq.questionTitle = 'questionTitle'; - faq.questionAnswer = 'questionAnswer'; + faq.questionTitle = 'questionTitle ' + id; + faq.questionAnswer = 'questionAnswer ' + id; faq.categories = [new FaqCategory(category, color)]; return faq; } @@ -84,6 +84,9 @@ describe('CourseFaqs', () => { applyFilters: () => { return [faq2, faq3]; }, + hasSearchTokens: () => { + return true; + }, }), ], }) @@ -125,6 +128,20 @@ describe('CourseFaqs', () => { expect(courseFaqComponent.filteredFaqs).toEqual([faq2, faq3]); }); + it('should search through already filtered array', () => { + const searchSpy = jest.spyOn(faqService, 'hasSearchTokens'); + const applyFilterSpy = jest.spyOn(faqService, 'applyFilters'); + courseFaqComponent.setSearchValue('questionTitle'); + courseFaqComponent.defineSearchedAndFilteredFaq(courseFaqComponent.searchInput.getValue()); + expect(applyFilterSpy).toHaveBeenCalledOnce(); + expect(searchSpy).toHaveBeenCalledTimes(2); + expect(searchSpy).toHaveBeenCalledWith(faq2, 'questionTitle'); + expect(searchSpy).toHaveBeenCalledWith(faq3, 'questionTitle'); + expect(courseFaqComponent.filteredFaqs).toHaveLength(2); + expect(courseFaqComponent.filteredFaqs).not.toContain(faq1); + expect(courseFaqComponent.filteredFaqs).toEqual([faq2, faq3]); + }); + it('should catch error if no categories are found', () => { alertServiceStub = jest.spyOn(alertService, 'error'); const error = { status: 404 }; diff --git a/src/test/javascript/spec/service/faq.service.spec.ts b/src/test/javascript/spec/service/faq.service.spec.ts index b5612f991ebd..b121ec2a2c76 100644 --- a/src/test/javascript/spec/service/faq.service.spec.ts +++ b/src/test/javascript/spec/service/faq.service.spec.ts @@ -205,5 +205,14 @@ describe('Faq Service', () => { const convertedCategory = FaqService.stringifyFaqCategories(faq2); expect(convertedCategory).toEqual(['{"color":"red","category":"testing"}']); }); + + it('should return if all tokens exist in FAQ title or answer', () => { + const faq1 = new Faq(); + faq1.questionTitle = 'Title'; + faq1.questionAnswer = 'Answer'; + + expect(service.hasSearchTokens(faq1, 'title answer')).toBeTrue(); + expect(service.hasSearchTokens(faq1, 'title answer missing')).toBeFalse(); + }); }); }); From dc49ecb58a1b61737c9b2dd3247d7976c6b288e2 Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Mon, 7 Oct 2024 09:21:03 +0200 Subject: [PATCH 3/9] Coderabit, reduced debounce to 300ms --- src/main/webapp/app/faq/faq.service.ts | 13 +++++-------- .../app/overview/course-faq/course-faq.component.ts | 9 +++++---- .../course-faq/course-faq.component.spec.ts | 2 +- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/main/webapp/app/faq/faq.service.ts b/src/main/webapp/app/faq/faq.service.ts index c98253e5223e..bcdd824c671c 100644 --- a/src/main/webapp/app/faq/faq.service.ts +++ b/src/main/webapp/app/faq/faq.service.ts @@ -138,15 +138,12 @@ export class FaqService { } } - hasSearchTokens(faq: Faq, searchTerm: string) { - if (searchTerm == '') { + hasSearchTokens(faq: Faq, searchTerm: string): boolean { + if (searchTerm === '') { return true; } - const tokens = searchTerm.split(' '); - if (tokens) { - let faqText = faq.questionTitle + ' ' + faq.questionAnswer; - faqText = faqText.toLowerCase(); - return tokens.every((token) => faqText.includes(token.toLowerCase())); - } + const tokens = searchTerm.toLowerCase().split(' '); + const faqText = `${faq.questionTitle ?? ''} ${faq.questionAnswer ?? ''}`.toLowerCase(); + return tokens.every((token) => faqText.includes(token)); } } diff --git a/src/main/webapp/app/overview/course-faq/course-faq.component.ts b/src/main/webapp/app/overview/course-faq/course-faq.component.ts index db3cd62a23eb..03fd207fda21 100644 --- a/src/main/webapp/app/overview/course-faq/course-faq.component.ts +++ b/src/main/webapp/app/overview/course-faq/course-faq.component.ts @@ -57,8 +57,8 @@ export class CourseFaqComponent implements OnInit, OnDestroy { this.loadFaqs(); this.loadCourseExerciseCategories(this.courseId); }); - this.searchInput.pipe(debounceTime(500)).subscribe((searchTerm: string) => { - this.defineSearchedAndFilteredFaq(searchTerm); + this.searchInput.pipe(debounceTime(300)).subscribe((searchTerm: string) => { + this.refreshFaqList(searchTerm); }); } @@ -86,11 +86,12 @@ export class CourseFaqComponent implements OnInit, OnDestroy { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); this.parentParamSubscription?.unsubscribe(); + this.searchInput.complete(); } toggleFilters(category: string) { this.activeFilters = this.faqService.toggleFilter(category, this.activeFilters); - this.defineSearchedAndFilteredFaq(this.searchInput.getValue()); + this.refreshFaqList(this.searchInput.getValue()); } private applyFilters(): void { @@ -107,7 +108,7 @@ export class CourseFaqComponent implements OnInit, OnDestroy { this.searchInput.next(searchValue); } - defineSearchedAndFilteredFaq(searchTerm: string) { + refreshFaqList(searchTerm: string) { this.applyFilters(); this.applySearch(searchTerm); } diff --git a/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts b/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts index d7a9b7f40080..49f162e7c929 100644 --- a/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts @@ -132,7 +132,7 @@ describe('CourseFaqs', () => { const searchSpy = jest.spyOn(faqService, 'hasSearchTokens'); const applyFilterSpy = jest.spyOn(faqService, 'applyFilters'); courseFaqComponent.setSearchValue('questionTitle'); - courseFaqComponent.defineSearchedAndFilteredFaq(courseFaqComponent.searchInput.getValue()); + courseFaqComponent.refreshFaqList(courseFaqComponent.searchInput.getValue()); expect(applyFilterSpy).toHaveBeenCalledOnce(); expect(searchSpy).toHaveBeenCalledTimes(2); expect(searchSpy).toHaveBeenCalledWith(faq2, 'questionTitle'); From f0437512e6f893247c8d22cff9ae96627e99031f Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Mon, 7 Oct 2024 11:18:59 +0200 Subject: [PATCH 4/9] Coderabit, reduced debounce to 300ms, Fixed client tests --- .../component/course/course-exercises.component.spec.ts | 2 +- .../exam-navigation-sidebar.component.spec.ts | 3 ++- .../overview/course-exams/course-exams.component.spec.ts | 4 ++-- .../overview/course-faq/course-faq.component.spec.ts | 9 ++++++++- .../course-lectures/course-lectures.component.spec.ts | 2 +- .../component/shared/search-filter.component.spec.ts | 5 ++--- .../shared/sidebar/sidebar-accordion.component.spec.ts | 1 + .../course-tutorial-groups.component.spec.ts | 4 ++-- 8 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/test/javascript/spec/component/course/course-exercises.component.spec.ts b/src/test/javascript/spec/component/course/course-exercises.component.spec.ts index 3a2e57aa1588..4552d41d98bd 100644 --- a/src/test/javascript/spec/component/course/course-exercises.component.spec.ts +++ b/src/test/javascript/spec/component/course/course-exercises.component.spec.ts @@ -49,7 +49,6 @@ describe('CourseExercisesComponent', () => { declarations: [ CourseExercisesComponent, SidebarComponent, - SearchFilterComponent, MockDirective(OrionFilterDirective), MockComponent(CourseExerciseRowComponent), MockComponent(SidePanelComponent), @@ -61,6 +60,7 @@ describe('CourseExercisesComponent', () => { MockDirective(DeleteButtonDirective), MockTranslateValuesDirective, MockPipe(SearchFilterPipe), + MockComponent(SearchFilterComponent), ], providers: [ { provide: SessionStorageService, useClass: MockSyncStorage }, diff --git a/src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts b/src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts index 45ce49fc2b4d..932c2cba3571 100644 --- a/src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts +++ b/src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts @@ -20,6 +20,7 @@ import { TranslateService } from '@ngx-translate/core'; import { facSaveSuccess, facSaveWarning } from 'src/main/webapp/content/icons/icons'; import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { ExamLiveEventsButtonComponent } from 'app/exam/participate/events/exam-live-events-button.component'; +import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; describe('ExamNavigationSidebarComponent', () => { let fixture: ComponentFixture; @@ -34,7 +35,7 @@ describe('ExamNavigationSidebarComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ArtemisTestModule, TranslateTestingModule, MockModule(NgbTooltipModule)], - declarations: [ExamNavigationSidebarComponent, MockComponent(ExamTimerComponent), MockComponent(ExamLiveEventsButtonComponent)], + declarations: [ExamNavigationSidebarComponent, MockComponent(ExamTimerComponent), MockComponent(ExamLiveEventsButtonComponent), MockComponent(SearchFilterComponent)], providers: [ ExamParticipationService, { provide: ExamExerciseUpdateService, useValue: mockExamExerciseUpdateService }, diff --git a/src/test/javascript/spec/component/overview/course-exams/course-exams.component.spec.ts b/src/test/javascript/spec/component/overview/course-exams/course-exams.component.spec.ts index 60590bae4bde..9a76cf9e2713 100644 --- a/src/test/javascript/spec/component/overview/course-exams/course-exams.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-exams/course-exams.component.spec.ts @@ -5,7 +5,7 @@ import { CourseExamsComponent } from 'app/overview/course-exams/course-exams.com import { Exam } from 'app/entities/exam/exam.model'; import { ArtemisTestModule } from '../../../test.module'; import dayjs from 'dayjs/esm'; -import { MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; +import { MockComponent, MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { Observable, of } from 'rxjs'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { ExamParticipationService } from 'app/exam/participate/exam-participation.service'; @@ -102,7 +102,7 @@ describe('CourseExamsComponent', () => { TestBed.configureTestingModule({ imports: [ArtemisTestModule, RouterTestingModule, MockModule(FormsModule), MockModule(ReactiveFormsModule), MockDirective(TranslateDirective)], - declarations: [CourseExamsComponent, SidebarComponent, SearchFilterComponent, MockPipe(ArtemisTranslatePipe), MockPipe(SearchFilterPipe)], + declarations: [CourseExamsComponent, SidebarComponent, MockComponent(SearchFilterComponent), MockPipe(ArtemisTranslatePipe), MockPipe(SearchFilterPipe)], providers: [ { provide: Router, useValue: router }, { diff --git a/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts b/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts index 49f162e7c929..f7795a433603 100644 --- a/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts @@ -18,6 +18,7 @@ import { CustomExerciseCategoryBadgeComponent } from 'app/shared/exercise-catego import { CourseFaqAccordionComponent } from 'app/overview/course-faq/course-faq-accordion-component'; import { Faq } from 'app/entities/faq.model'; import { FaqCategory } from 'app/entities/faq-category.model'; +import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; function createFaq(id: number, category: string, color: string): Faq { const faq = new Faq(); @@ -48,7 +49,13 @@ describe('CourseFaqs', () => { TestBed.configureTestingModule({ imports: [ArtemisSharedComponentModule, ArtemisSharedModule, MockComponent(CustomExerciseCategoryBadgeComponent), MockComponent(CourseFaqAccordionComponent)], - declarations: [CourseFaqComponent, MockPipe(ArtemisTranslatePipe), MockComponent(FaIconComponent), MockDirective(TranslateDirective)], + declarations: [ + CourseFaqComponent, + MockPipe(ArtemisTranslatePipe), + MockComponent(FaIconComponent), + MockDirective(TranslateDirective), + MockComponent(SearchFilterComponent), + ], providers: [ MockProvider(FaqService), { provide: Router, useClass: MockRouter }, diff --git a/src/test/javascript/spec/component/overview/course-lectures/course-lectures.component.spec.ts b/src/test/javascript/spec/component/overview/course-lectures/course-lectures.component.spec.ts index 55048ea53f59..e95b359e2be4 100644 --- a/src/test/javascript/spec/component/overview/course-lectures/course-lectures.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-lectures/course-lectures.component.spec.ts @@ -72,13 +72,13 @@ describe('CourseLectures', () => { CourseLecturesComponent, CourseLectureRowStubComponent, SidebarComponent, - SearchFilterComponent, MockPipe(ArtemisTranslatePipe), MockPipe(ArtemisDatePipe), MockPipe(SearchFilterPipe), MockComponent(SidePanelComponent), MockComponent(FaIconComponent), MockDirective(TranslateDirective), + MockComponent(SearchFilterComponent), ], providers: [ MockProvider(CourseStorageService, { diff --git a/src/test/javascript/spec/component/shared/search-filter.component.spec.ts b/src/test/javascript/spec/component/shared/search-filter.component.spec.ts index 5ff83d37e5fd..a75e50a3551e 100644 --- a/src/test/javascript/spec/component/shared/search-filter.component.spec.ts +++ b/src/test/javascript/spec/component/shared/search-filter.component.spec.ts @@ -1,11 +1,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTestModule } from '../../test.module'; import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; -import { MockModule, MockPipe } from 'ng-mocks'; +import { MockModule } from 'ng-mocks'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { By } from '@angular/platform-browser'; -import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; describe('SearchFilterComponent', () => { let component: SearchFilterComponent; @@ -14,7 +13,7 @@ describe('SearchFilterComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [ArtemisTestModule, MockModule(ReactiveFormsModule), MockModule(FormsModule), MockModule(FontAwesomeModule)], - declarations: [SearchFilterComponent, MockPipe(ArtemisTranslatePipe)], + declarations: [SearchFilterComponent], }).compileComponents(); }); diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts index bcffb82bc9c1..68eddc94218d 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts @@ -31,6 +31,7 @@ describe('SidebarAccordionComponent', () => { SearchFilterPipe, SearchFilterComponent, MockPipe(ArtemisTranslatePipe), + MockComponent(SearchFilterComponent), ], }).compileComponents(); }); diff --git a/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts b/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts index cf60468091e9..95738abdc115 100644 --- a/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts +++ b/src/test/javascript/spec/component/tutorial-groups/course-tutorial-groups/course-tutorial-groups.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; import { MockRouter } from '../../../helpers/mocks/mock-router'; -import { MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; +import { MockComponent, MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { AlertService } from 'app/core/util/alert.service'; import { ActivatedRoute, Router, RouterModule, convertToParamMap } from '@angular/router'; @@ -34,7 +34,7 @@ describe('CourseTutorialGroupsComponent', () => { TestBed.configureTestingModule({ imports: [ArtemisTestModule, RouterModule, MockModule(FormsModule), MockModule(ReactiveFormsModule), MockDirective(TranslateDirective)], - declarations: [CourseTutorialGroupsComponent, MockPipe(ArtemisTranslatePipe), SidebarComponent, SearchFilterComponent, MockPipe(SearchFilterPipe)], + declarations: [CourseTutorialGroupsComponent, MockPipe(ArtemisTranslatePipe), SidebarComponent, MockComponent(SearchFilterComponent), MockPipe(SearchFilterPipe)], providers: [ MockProvider(TutorialGroupsService), MockProvider(CourseStorageService), From 9096f06139cb894398abdaca1181c4c395b4c40e Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Wed, 9 Oct 2024 15:21:19 +0200 Subject: [PATCH 5/9] Add searchBar to management --- src/main/webapp/app/faq/faq.component.html | 5 ++++- src/main/webapp/app/faq/faq.component.ts | 26 +++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/app/faq/faq.component.html b/src/main/webapp/app/faq/faq.component.html index 94bdab39fb48..e340e22f764d 100644 --- a/src/main/webapp/app/faq/faq.component.html +++ b/src/main/webapp/app/faq/faq.component.html @@ -5,7 +5,10 @@

-
+
+
+ +
-
+
From 8b0b9ec6e48530d70276af24ad534c15a52b5ac4 Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Wed, 9 Oct 2024 15:39:58 +0200 Subject: [PATCH 7/9] unsubscribe subject --- src/main/webapp/app/faq/faq.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/webapp/app/faq/faq.component.ts b/src/main/webapp/app/faq/faq.component.ts index 5ba868dc78ed..07ba001f26b6 100644 --- a/src/main/webapp/app/faq/faq.component.ts +++ b/src/main/webapp/app/faq/faq.component.ts @@ -68,6 +68,7 @@ export class FaqComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.dialogErrorSource.complete(); + this.searchInput.complete(); } deleteFaq(courseId: number, faqId: number) { From 0e4f14c04a5b3e6baee357cc9bd091a85e7e0fe4 Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Wed, 9 Oct 2024 16:12:02 +0200 Subject: [PATCH 8/9] Added tests, fixed minor issue --- src/main/webapp/app/faq/faq.component.ts | 2 +- .../spec/component/faq/faq.component.spec.ts | 26 ++++++++++++++----- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/app/faq/faq.component.ts b/src/main/webapp/app/faq/faq.component.ts index 07ba001f26b6..0a8c1df808f8 100644 --- a/src/main/webapp/app/faq/faq.component.ts +++ b/src/main/webapp/app/faq/faq.component.ts @@ -86,7 +86,7 @@ export class FaqComponent implements OnInit, OnDestroy { toggleFilters(category: string) { this.activeFilters = this.faqService.toggleFilter(category, this.activeFilters); - this.applyFilters(); + this.refreshFaqList(this.searchInput.getValue()); } private applyFilters(): void { diff --git a/src/test/javascript/spec/component/faq/faq.component.spec.ts b/src/test/javascript/spec/component/faq/faq.component.spec.ts index d31f60b8e2f2..d9f8b84c6cd5 100644 --- a/src/test/javascript/spec/component/faq/faq.component.spec.ts +++ b/src/test/javascript/spec/component/faq/faq.component.spec.ts @@ -1,5 +1,5 @@ import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { ComponentFixture, TestBed, fakeAsync, flush } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateService } from '@ngx-translate/core'; import { ActivatedRoute, Router, convertToParamMap } from '@angular/router'; import { MockComponent, MockModule, MockProvider } from 'ng-mocks'; @@ -93,6 +93,9 @@ describe('FaqComponent', () => { applyFilters: () => { return [faq2, faq3]; }, + hasSearchTokens: () => { + return true; + }, }), ], }) @@ -158,20 +161,31 @@ describe('FaqComponent', () => { expect(faqComponent.filteredFaqs).toEqual([faq2, faq3]); }); - it('should catch error if no categories are found', fakeAsync(() => { + it('should catch error if no categories are found', () => { alertServiceStub = jest.spyOn(alertService, 'error'); const error = { status: 404 }; jest.spyOn(faqService, 'findAllCategoriesByCourseId').mockReturnValue(throwError(() => new HttpErrorResponse(error))); faqComponentFixture.detectChanges(); expect(alertServiceStub).toHaveBeenCalledOnce(); - flush(); - })); + }); + + it('should search through already filtered array', () => { + const searchSpy = jest.spyOn(faqService, 'hasSearchTokens'); + const applyFilterSpy = jest.spyOn(faqService, 'applyFilters'); + faqComponent.setSearchValue('questionTitle'); + faqComponent.refreshFaqList(faqComponent.searchInput.getValue()); + expect(applyFilterSpy).toHaveBeenCalledOnce(); + expect(searchSpy).toHaveBeenCalledTimes(2); + expect(searchSpy).toHaveBeenCalledWith(faq2, 'questionTitle'); + expect(searchSpy).toHaveBeenCalledWith(faq3, 'questionTitle'); + expect(faqComponent.filteredFaqs).toHaveLength(2); + expect(faqComponent.filteredFaqs).not.toContain(faq1); + expect(faqComponent.filteredFaqs).toEqual([faq2, faq3]); + }); it('should call sortService when sortRows is called', () => { jest.spyOn(sortService, 'sortByProperty').mockReturnValue([]); - faqComponent.sortRows(); - expect(sortService.sortByProperty).toHaveBeenCalledOnce(); }); }); From 362c79801d3abd5cc43b5576799260755a7f373f Mon Sep 17 00:00:00 2001 From: Tim Cremer Date: Fri, 11 Oct 2024 12:14:27 +0200 Subject: [PATCH 9/9] Added import --- src/main/webapp/app/overview/courses.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/webapp/app/overview/courses.module.ts b/src/main/webapp/app/overview/courses.module.ts index 01e27a171163..5dbae0d30fde 100644 --- a/src/main/webapp/app/overview/courses.module.ts +++ b/src/main/webapp/app/overview/courses.module.ts @@ -19,6 +19,7 @@ import { ArtemisCourseExerciseRowModule } from 'app/overview/course-exercises/co import { NgxChartsModule, PieChartModule } from '@swimlane/ngx-charts'; import { CourseUnenrollmentModalComponent } from 'app/overview/course-unenrollment-modal.component'; import { ArtemisSidebarModule } from 'app/shared/sidebar/sidebar.module'; +import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; @NgModule({ imports: [ @@ -36,6 +37,7 @@ import { ArtemisSidebarModule } from 'app/shared/sidebar/sidebar.module'; NgxChartsModule, PieChartModule, ArtemisSidebarModule, + SearchFilterComponent, ], declarations: [ CoursesComponent,