Skip to content
Snippets Groups Projects
Commit 10e23218 authored by Art Lowel's avatar Art Lowel Committed by GitHub
Browse files

Merge pull request #182 from rivaldi8/bug-171-list-grid-view-switcher

Bug 171:  List grid view switcher
parents 311fef21 cbac9ab1
Branches
Tags
No related merge requests found
Showing with 231 additions and 2 deletions
......@@ -81,6 +81,10 @@
},
"results": {
"title": "Search Results"
},
"view-switch": {
"show-list": "Show as list",
"show-grid": "Show as grid"
}
},
"loading": {
......
import { SortOptions } from '../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
export enum ViewMode {
List = 'list',
Grid = 'grid'
}
export class SearchOptions {
pagination?: PaginationComponentOptions;
sort?: SortOptions;
view?: ViewMode = ViewMode.List;
}
......@@ -5,5 +5,6 @@
[currentParams]="currentParams"
[scopes]="scopeList?.payload">
</ds-search-form>
<ds-view-mode-switch></ds-view-mode-switch>
<ds-search-results [searchResults]="results" [searchConfig]="searchOptions"></ds-search-results>
</div>
......@@ -6,6 +6,7 @@ import { SearchResult } from './search-result.model';
import { DSpaceObject } from '../core/shared/dspace-object.model';
import { SortOptions } from '../core/cache/models/sort-options.model';
import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model';
import { ViewModeSwitchComponent } from '../shared/view-mode-switch/view-mode-switch.component';
import { SearchOptions } from './search-options.model';
import { CommunityDataService } from '../core/data/community-data.service';
import { isNotEmpty } from '../shared/empty.util';
......
import { TestBed, fakeAsync, tick } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { SearchService } from './search.service';
import { ItemDataService } from './../../core/data/item-data.service';
import { ViewMode } from '../../+search-page/search-options.model';
@Component({ template: '' })
class DummyComponent { }
describe('SearchService', () => {
let searchService: SearchService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
CommonModule,
RouterTestingModule.withRoutes([
{ path: 'search', component: DummyComponent, pathMatch: 'full' },
])
],
declarations: [
DummyComponent
],
providers: [
{ provide: ItemDataService, useValue: {} },
SearchService
],
});
searchService = TestBed.get(SearchService);
});
it('should return list view mode by default', () => {
searchService.getViewMode().subscribe((viewMode) => {
expect(viewMode).toBe(ViewMode.List);
});
});
it('should return the view mode set through setViewMode', fakeAsync(() => {
searchService.setViewMode(ViewMode.Grid)
tick();
let viewMode = ViewMode.List;
searchService.getViewMode().subscribe((mode) => viewMode = mode);
expect(viewMode).toBe(ViewMode.Grid);
searchService.setViewMode(ViewMode.List)
tick();
searchService.getViewMode().subscribe((mode) => viewMode = mode);
expect(viewMode).toBe(ViewMode.List);
}));
});
......@@ -13,6 +13,8 @@ import { ItemSearchResult } from '../../object-list/search-result-list-element/i
import { SearchFilterConfig } from './search-filter-config.model';
import { FilterType } from './filter-type.model';
import { FacetValue } from './facet-value.model';
import { ViewMode } from '../../+search-page/search-options.model';
import { Router, NavigationExtras, ActivatedRoute } from '@angular/router';
function shuffle(array: any[]) {
let i = 0;
......@@ -76,7 +78,10 @@ export class SearchService {
})
];
constructor(private itemDataService: ItemDataService) {
constructor(
private itemDataService: ItemDataService,
private route: ActivatedRoute,
private router: Router) {
}
......@@ -192,4 +197,23 @@ export class SearchService {
Observable.of(values)
);
}
getViewMode(): Observable<ViewMode> {
return this.route.queryParams.map((params) => {
if (isNotEmpty(params.view) && hasValue(params.view)) {
return params.view;
} else {
return ViewMode.List;
}
});
}
setViewMode(viewMode: ViewMode) {
const navigationExtras: NavigationExtras = {
queryParams: {view: viewMode},
queryParamsHandling: 'merge'
};
this.router.navigate(['/search'], navigationExtras);
}
}
......@@ -29,6 +29,7 @@ import { ThumbnailComponent } from '../thumbnail/thumbnail.component';
import { SearchResultListElementComponent } from '../object-list/search-result-list-element/search-result-list-element.component';
import { SearchFormComponent } from './search-form/search-form.component';
import { WrapperListElementComponent } from '../object-list/wrapper-list-element/wrapper-list-element.component';
import { ViewModeSwitchComponent } from './view-mode-switch/view-mode-switch.component';
const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
......@@ -61,7 +62,8 @@ const COMPONENTS = [
PaginationComponent,
SearchFormComponent,
ThumbnailComponent,
WrapperListElementComponent
WrapperListElementComponent,
ViewModeSwitchComponent
];
const ENTRY_COMPONENTS = [
......
<div class="btn-group" data-toggle="buttons">
<a routerLink="."
[queryParams]="{view: 'list'}"
queryParamsHandling="merge"
(click)="switchViewTo(viewModeEnum.List)"
routerLinkActive="active"
[class.active]="currentMode === viewModeEnum.List"
class="btn btn-secondary">
<i class="fa fa-list" title="{{'search.view-switch.show-list' | translate}}"></i>
</a>
<a routerLink="."
[queryParams]="{view: 'grid'}"
queryParamsHandling="merge"
(click)="switchViewTo(viewModeEnum.Grid)"
routerLinkActive="active"
[class.active]="currentMode !== viewModeEnum.List"
class="btn btn-secondary">
<i class="fa fa-th-large" title="{{'search.view-switch.show-grid' | translate}}"></i>
</a>
</div>
\ No newline at end of file
@import '../../../styles/variables.scss';
\ No newline at end of file
import { DebugElement } from '@angular/core';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { MockTranslateLoader } from '../mocks/mock-translate-loader';
import { RouterTestingModule } from '@angular/router/testing';
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { SearchService } from '../../+search-page/search-service/search.service';
import { ItemDataService } from './../../core/data/item-data.service';
import { ViewModeSwitchComponent } from './view-mode-switch.component';
import { ViewMode } from '../../+search-page/search-options.model';
@Component({ template: '' })
class DummyComponent { }
describe('ViewModeSwitchComponent', () => {
let comp: ViewModeSwitchComponent;
let fixture: ComponentFixture<ViewModeSwitchComponent>;
let searchService: SearchService;
let listButton: HTMLElement;
let gridButton: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: MockTranslateLoader
}
}),
RouterTestingModule.withRoutes([
{ path: 'search', component: DummyComponent, pathMatch: 'full' },
])
],
declarations: [
ViewModeSwitchComponent,
DummyComponent
],
providers: [
{ provide: ItemDataService, useValue: {} },
SearchService
],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ViewModeSwitchComponent);
comp = fixture.componentInstance; // ViewModeSwitchComponent test instance
fixture.detectChanges();
const debugElements = fixture.debugElement.queryAll(By.css('a'));
listButton = debugElements[0].nativeElement;
gridButton = debugElements[1].nativeElement;
searchService = fixture.debugElement.injector.get(SearchService);
});
it('should set list button as active when on list mode', fakeAsync(() => {
searchService.setViewMode(ViewMode.List);
tick();
fixture.detectChanges();
expect(comp.currentMode).toBe(ViewMode.List);
expect(listButton.classList).toContain('active');
expect(gridButton.classList).not.toContain('active');
}));
it('should set grid button as active when on grid mode', fakeAsync(() => {
searchService.setViewMode(ViewMode.Grid);
tick();
fixture.detectChanges();
expect(comp.currentMode).toBe(ViewMode.Grid);
expect(listButton.classList).not.toContain('active');
expect(gridButton.classList).toContain('active');
}));
});
import { Subscription } from 'rxjs/Subscription';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ViewMode } from '../../+search-page/search-options.model';
import { SearchService } from './../../+search-page/search-service/search.service';
/**
* Component to switch between list and grid views.
*/
@Component({
selector: 'ds-view-mode-switch',
styleUrls: ['./view-mode-switch.component.scss'],
templateUrl: './view-mode-switch.component.html'
})
export class ViewModeSwitchComponent implements OnInit, OnDestroy {
currentMode: ViewMode = ViewMode.List;
viewModeEnum = ViewMode;
private sub: Subscription;
constructor(private searchService: SearchService) {
}
ngOnInit(): void {
this.sub = this.searchService.getViewMode().subscribe((viewMode: ViewMode) => {
this.currentMode = viewMode;
});
}
switchViewTo(viewMode: ViewMode) {
this.searchService.setViewMode(viewMode);
}
ngOnDestroy() {
if (this.sub !== undefined) {
this.sub.unsubscribe();
}
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment