diff --git a/aio/src/app/custom-elements/resource/resource-list.component.html b/aio/src/app/custom-elements/resource/resource-list.component.html index e5b861caeb..e58b70287c 100644 --- a/aio/src/app/custom-elements/resource/resource-list.component.html +++ b/aio/src/app/custom-elements/resource/resource-list.component.html @@ -1,28 +1,28 @@
-
-
-
- -

{{category.title}}

-
+ +
+
+ diff --git a/aio/src/app/custom-elements/resource/resource-list.component.spec.ts b/aio/src/app/custom-elements/resource/resource-list.component.spec.ts index c42ae7d01a..60a9374252 100644 --- a/aio/src/app/custom-elements/resource/resource-list.component.spec.ts +++ b/aio/src/app/custom-elements/resource/resource-list.component.spec.ts @@ -1,78 +1,115 @@ import { ReflectiveInjector } from '@angular/core'; -import { PlatformLocation } from '@angular/common'; import { of } from 'rxjs'; import { ResourceListComponent } from './resource-list.component'; import { ResourceService } from './resource.service'; - +import { LocationService } from 'app/shared/location.service'; import { Category } from './resource.model'; // Testing the component class behaviors, independent of its template // Let e2e tests verify how it displays. describe('ResourceListComponent', () => { + let component: ResourceListComponent; let injector: ReflectiveInjector; - let location: TestPlatformLocation; + let resourceService: TestResourceService; + let locationService: TestLocationService; + let categories: Category[]; beforeEach(() => { injector = ReflectiveInjector.resolveAndCreate([ ResourceListComponent, - {provide: PlatformLocation, useClass: TestPlatformLocation }, - {provide: ResourceService, useClass: TestResourceService } + {provide: ResourceService, useClass: TestResourceService }, + {provide: LocationService, useClass: TestLocationService } ]); - location = injector.get(PlatformLocation); + locationService = injector.get(LocationService); + resourceService = injector.get(ResourceService); + categories = resourceService.testCategories; }); - it('should set the location w/o leading slashes', () => { - location.pathname = '////resources'; - const component = getComponent(); - expect(component.location).toBe('resources'); + it('should select the first category when no query string', () => { + component = getComponent(); + expect(component.selectedCategory).toBe(categories[0]); }); - it('href(id) should return the expected href', () => { - location.pathname = '////resources'; - const component = getComponent(); - expect(component.href({id: 'foo'})).toBe('resources#foo'); + it('should select the first category when query string w/o "category" property', () => { + locationService.searchResult = { foo: 'development' }; + component = getComponent(); + expect(component.selectedCategory).toBe(categories[0]); }); - it('should set scroll position to zero when no target element', () => { - const component = getComponent(); - component.onScroll(undefined); - expect(component.scrollPos).toBe(0); + it('should select the first category when query category not found', () => { + locationService.searchResult = { category: 'foo' }; + component = getComponent(); + expect(component.selectedCategory).toBe(categories[0]); }); - it('should set scroll position to element.scrollTop when that is defined', () => { - const component = getComponent(); - component.onScroll({scrollTop: 42}); - expect(component.scrollPos).toBe(42); + it('should select the education category when query category is "education"', () => { + locationService.searchResult = { category: 'education' }; + component = getComponent(); + expect(component.selectedCategory).toBe(categories[1]); }); - it('should set scroll position to element.body.scrollTop when that is defined', () => { - const component = getComponent(); - component.onScroll({body: {scrollTop: 42}}); - expect(component.scrollPos).toBe(42); + it('should select the education category when query category is "EDUCATION" (case insensitive)', () => { + locationService.searchResult = { category: 'EDUCATION' }; + component = getComponent(); + expect(component.selectedCategory).toBe(categories[1]); }); - it('should set scroll position to 0 when no target.body.scrollTop defined', () => { - const component = getComponent(); - component.onScroll({body: {}}); - expect(component.scrollPos).toBe(0); + it('should set the query to the "education" category when user selects "education"', () => { + component = getComponent(); + component.selectCategory('education'); + expect(locationService.searchResult['category']).toBe('education'); + }); + + it('should set the query to the first category when user selects unknown name', () => { + component = getComponent(); + component.selectCategory('education'); // a legit group that isn't the first + + component.selectCategory('foo'); // not a legit group name + expect(locationService.searchResult['category']).toBe('development'); }); //// Test Helpers //// - function getComponent(): ResourceListComponent { return injector.get(ResourceListComponent); } - - class TestPlatformLocation { - pathname = 'resources'; + function getComponent(): ResourceListComponent { + const comp = injector.get(ResourceListComponent); + comp.ngOnInit(); + return comp; } class TestResourceService { - categories = of(getTestData); + testCategories = getTestData(); + categories = of(this.testCategories); + } + + interface SearchResult { [index: string]: string; } + + class TestLocationService { + searchResult: SearchResult = {}; + search = jasmine.createSpy('search').and.callFake(() => this.searchResult); + setSearch = jasmine.createSpy('setSearch') + .and.callFake((_label: string, result: SearchResult) => { + this.searchResult = result; + }); } function getTestData(): Category[] { - return []; // Not interested in the data in these tests + return [ + // Not interested in the sub-categories data in these tests + { + id: 'development', + title: 'Development', + order: 0, + subCategories: [] + }, + { + id: 'education', + title: 'Education', + order: 1, + subCategories: [] + }, + ]; } }); diff --git a/aio/src/app/custom-elements/resource/resource-list.component.ts b/aio/src/app/custom-elements/resource/resource-list.component.ts index 6d9e4671a3..6b48195fa8 100644 --- a/aio/src/app/custom-elements/resource/resource-list.component.ts +++ b/aio/src/app/custom-elements/resource/resource-list.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { Category } from './resource.model'; import { ResourceService } from './resource.service'; +import { LocationService } from 'app/shared/location.service'; /* tslint:disable:template-accessibility-elements-content */ @Component({ @@ -11,16 +12,27 @@ import { ResourceService } from './resource.service'; export class ResourceListComponent implements OnInit { categories: Category[]; - location: string; + selectedCategory: Category; constructor( - private resourceService: ResourceService) { - this.location = location.pathname.replace(/^\/+/, ''); + private resourceService: ResourceService, + private locationService: LocationService) { } ngOnInit() { + const category = this.locationService.search()['category'] || ''; // Not using async pipe because cats appear twice in template // No need to unsubscribe because categories observable completes. - this.resourceService.categories.subscribe(cats => this.categories = cats); + this.resourceService.categories.subscribe(cats => { + this.categories = cats; + this.selectCategory(category); + }); + } + + selectCategory(id: string) { + id = id.toLowerCase(); + this.selectedCategory = + this.categories.find(category => category.id.toLowerCase() === id) || this.categories[0]; + this.locationService.setSearch('', {category: this.selectedCategory.id}); } } diff --git a/aio/src/styles/2-modules/_resources.scss b/aio/src/styles/2-modules/_resources.scss index 386eac2829..890f051e96 100644 --- a/aio/src/styles/2-modules/_resources.scss +++ b/aio/src/styles/2-modules/_resources.scss @@ -169,6 +169,10 @@ aio-resource-list { flex-direction: column; } + .align-items-center{ + align-items: center; + } + .c-resource-header { margin-bottom: 16px; }