Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit 043cb80

Browse files
committed
docs:(created virtual grid cookbook)
docs: update component name to have Component suffix + update display value for path in code samples data.json rc img new image core lint
1 parent b669e30 commit 043cb80

File tree

18 files changed

+521
-0
lines changed

18 files changed

+521
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/// <reference path='../_protractor/e2e.d.ts' />
2+
'use strict';
3+
4+
describe('Virtual Grid', function () {
5+
6+
beforeEach(function () {
7+
browser.get('');
8+
});
9+
10+
it('should sort the grid', function() {
11+
ValidateRankingColumn(['1000', '999', '998', '997', '996', '995', '994', '993', '992', '991', '990']);
12+
13+
// click Ranking column and make sure values are sorted
14+
element(by.xpath('//tbody/tr[1]/td[3]')).click().then(function(){
15+
ValidateRankingColumn(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']);
16+
});
17+
});
18+
19+
function ValidateRankingColumn(range: string[]) {
20+
for (let i = 0; i < range.length; i++) {
21+
let rankCell = element.all(by.xpath('//tbody/tr[' + (i + 2) + ']/td[3]/input')).get(0);
22+
expect(rankCell.getAttribute('value')).toEqual(range[i]);
23+
}
24+
}
25+
26+
it('should bind to new row but keep DOM constant', function () {
27+
28+
let lastRowFirstTextCell = element.all(by.css('#cell10-0')).get(0);
29+
30+
// check that the last row number in the grid is 10
31+
expect(element(by.xpath('//tbody/tr[12]/td[1][contains(text(),10)]')).isPresent()).toBe(true);
32+
33+
let trs = element.all(by.xpath('//tbody/tr'));
34+
expect(trs.count()).toBe(12);
35+
36+
lastRowFirstTextCell.click().then(function(){
37+
lastRowFirstTextCell.clear();
38+
lastRowFirstTextCell.sendKeys('New Hero');
39+
expect(lastRowFirstTextCell.getAttribute('value')).toEqual('New Hero');
40+
41+
// page down one row
42+
lastRowFirstTextCell.sendKeys(protractor.Key.ARROW_DOWN);
43+
// navigating does not increase the number of rows
44+
trs = element.all(by.xpath('//tbody/tr'));
45+
expect(trs.count()).toBe(12);
46+
});
47+
});
48+
});
49+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
**/*.js
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Component } from '@angular/core';
2+
3+
import { HeroGridComponent } from './hero-grid.component';
4+
5+
@Component({
6+
selector: 'my-app',
7+
template: '<hero-grid></hero-grid>',
8+
directives: [HeroGridComponent]
9+
})
10+
11+
export class AppComponent {
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// #docregion
2+
import { Row } from './row';
3+
4+
export class Column {
5+
cellValue: string;
6+
row: Row;
7+
8+
constructor(public columnIndex: number, row: Row) {
9+
this.cellValue = '';
10+
this.row = row;
11+
}
12+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// #docregion
2+
import { Row } from './row';
3+
4+
export class HeroDataService {
5+
6+
getApplicants(count: number): Array<Row> {
7+
8+
let rows: Array<Row> = [];
9+
let heroes: Array<string> = ['Mr. Nice',
10+
'Narco',
11+
'Bombasto',
12+
'Celeritas',
13+
'Magneta',
14+
'RubberMan',
15+
'Dynama',
16+
'Dr IQ',
17+
'Magma',
18+
'Tornado'];
19+
20+
for (let i = 0; i < count; i++) {
21+
let heroIndex = this.generateRandomNumber(heroes.length - 1);
22+
let heroData = [heroes[heroIndex], count - i, this.generateRandomNumber(70) + 30];
23+
rows.push(this.createCell(i, heroData));
24+
}
25+
return rows;
26+
}
27+
28+
private generateRandomNumber(upperBound: number) {
29+
return Math.floor(Math.random() * upperBound);
30+
}
31+
32+
private createCell(index: number, values: Array<any>): Row {
33+
let row = new Row(values.length);
34+
35+
for (let i = 0; i < values.length; i++) {
36+
row.columns[i].cellValue = values[i];
37+
}
38+
return row;
39+
}
40+
}
41+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// #docregion
2+
import { Injectable } from '@angular/core';
3+
4+
import { Row } from './row';
5+
6+
@Injectable()
7+
export class HeroGridSortingService {
8+
sortDirection: number = 1;
9+
10+
sort(rows: Array<Row>, colIndex: number) {
11+
this.sortDirection *= -1;
12+
rows.sort((a, b) => {
13+
if (a.columns[colIndex].cellValue === b.columns[colIndex].cellValue) {
14+
return 0;
15+
}
16+
17+
if (a.columns[colIndex].cellValue > b.columns[colIndex].cellValue) {
18+
return -1 * this.sortDirection;
19+
}
20+
21+
if (a.columns[colIndex].cellValue < b.columns[colIndex].cellValue) {
22+
return 1 * this.sortDirection;
23+
}
24+
});
25+
}
26+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// #docregion
2+
import { Component, AfterViewChecked, ElementRef } from '@angular/core';
3+
4+
import { HeroGridSortingService } from './hero-grid-sorting.service';
5+
import { HeroGridService } from './hero-grid.service';
6+
import { KeyCodeService } from './key-code.service';
7+
import { HeroDataService } from './hero-data.service';
8+
import { Row } from './row';
9+
10+
@Component({
11+
selector: 'hero-grid',
12+
providers: [HeroGridService, KeyCodeService, HeroDataService, HeroGridSortingService],
13+
template: `<h1>Hero Grid</h1>
14+
<table id="hero-grid">
15+
<tr>
16+
<td class="row-number-column"></td>
17+
<td (click)="sort(colIndex)" class="columnHeader"
18+
*ngFor="#columnHeader of heroGridService.header; #colIndex = index">
19+
{{columnHeader}}
20+
</td>
21+
</tr>
22+
<tr *ngFor="#row of visibleRows">
23+
<td class="row-number-column">
24+
{{heroGridService.rows.indexOf(row)}}
25+
</td>
26+
<td *ngFor="#col of row.columns">
27+
<input [id]="heroGridService.createCellSelector(row,col)"
28+
[value]="col.cellValue" (input)="col.cellValue = $event.target.value"
29+
(click)="heroGridService.selectColumn(col)" (keydown)="navigate($event)" />
30+
</td>
31+
</tr>
32+
</table>`
33+
})
34+
35+
export class HeroGridComponent implements AfterViewChecked {
36+
37+
visibleRows: Array<Row> = [];
38+
heroGridService: HeroGridService;
39+
elementRef: ElementRef;
40+
41+
constructor(heroGridService: HeroGridService, elementRef: ElementRef) {
42+
this.elementRef = elementRef;
43+
this.heroGridService = heroGridService;
44+
this.visibleRows = this.heroGridService.getVisibleRows();
45+
}
46+
47+
navigate($event: any) {
48+
let res = this.heroGridService.navigate($event.keyCode);
49+
this.visibleRows = this.heroGridService.getVisibleRows();
50+
return res;
51+
}
52+
53+
sort(columnIndex: number) {
54+
this.heroGridService.sort(columnIndex);
55+
this.visibleRows = this.heroGridService.getVisibleRows();
56+
}
57+
58+
ngAfterViewChecked() {
59+
let cell = this.elementRef.nativeElement.querySelector('#' + this.heroGridService.getCurrentCellSelector());
60+
cell.focus();
61+
}
62+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// #docregion
2+
import { Injectable } from '@angular/core';
3+
4+
import { Column } from './column';
5+
import { HeroDataService } from './hero-data.service';
6+
import { HeroGridSortingService } from './hero-grid-sorting.service';
7+
import { KeyCodeService } from './key-code.service';
8+
import { Row } from './row';
9+
10+
@Injectable()
11+
export class HeroGridService {
12+
static maxRows = 1000;
13+
header: Array<string> = ['Name', 'Ranking', 'Age'];
14+
rows: Array<Row>;
15+
currentColumn: Column;
16+
currentRowIndex: number = 0;
17+
18+
private gridWindow: any;
19+
20+
keyCodeService: KeyCodeService;
21+
sortingService: HeroGridSortingService;
22+
23+
constructor(keyCodeService: KeyCodeService, heroDataService: HeroDataService, sortingService: HeroGridSortingService) {
24+
this.keyCodeService = keyCodeService;
25+
this.rows = heroDataService.getApplicants(HeroGridService.maxRows);
26+
this.sortingService = sortingService;
27+
28+
this.init();
29+
30+
let missingRows = this.gridWindow.pageSize - this.rows.length;
31+
32+
for (let i = 0; i <= missingRows; i++) {
33+
this.rows.push(new Row(this.header.length));
34+
}
35+
}
36+
37+
selectColumn(col: Column) {
38+
this.currentColumn = col;
39+
this.currentRowIndex = this.rows.indexOf(this.currentColumn.row);
40+
}
41+
42+
sort(colIndex: number) {
43+
this.sortingService.sort(this.rows, colIndex);
44+
this.init();
45+
}
46+
47+
createCellSelector(row: Row, col: Column): string {
48+
return 'cell' + this.rows.indexOf(row) + '-' + row.columns.indexOf(col);
49+
}
50+
51+
getCurrentCellSelector() {
52+
let cellIndex = this.rows[this.currentRowIndex].columns.indexOf(this.currentColumn);
53+
return 'cell' + this.currentRowIndex + '-' + cellIndex;
54+
}
55+
56+
getVisibleRows() {
57+
let visible: Array<Row> = [];
58+
for (let i = this.gridWindow.start; i <= this.gridWindow.end; i++) {
59+
visible.push(this.rows[i]);
60+
}
61+
return visible;
62+
}
63+
64+
navigate(keyCode: number) {
65+
let navDirection = this.keyCodeService.getNavigationKey(keyCode);
66+
67+
if (navDirection.down) {
68+
this.ensureRow();
69+
this.currentColumn = this.rows[this.currentRowIndex + 1].columns[this.currentColumn.columnIndex];
70+
this.adjustRowRangeDownward();
71+
}
72+
73+
if (navDirection.up) {
74+
if (this.currentRowIndex > 0) {
75+
this.currentColumn = this.rows[this.currentRowIndex - 1].columns[this.currentColumn.columnIndex];
76+
this.adjustRowRangeUpward();
77+
}
78+
}
79+
80+
if (navDirection.left) {
81+
if (this.currentColumn.columnIndex > 0) {
82+
this.currentColumn = this.rows[this.currentRowIndex].columns[this.currentColumn.columnIndex - 1];
83+
}
84+
}
85+
86+
if (navDirection.right) {
87+
if (this.currentColumn.columnIndex < this.header.length - 1) {
88+
this.currentColumn = this.rows[this.currentRowIndex].columns[this.currentColumn.columnIndex + 1];
89+
}
90+
}
91+
92+
if (navDirection.tab) {
93+
return false;
94+
}
95+
96+
this.currentRowIndex = this.rows.indexOf(this.currentColumn.row);
97+
return true;
98+
}
99+
100+
private adjustRowRangeUpward() {
101+
if (this.currentRowIndex <= this.gridWindow.start) {
102+
this.shiftRowsBy(-1);
103+
}
104+
}
105+
106+
private adjustRowRangeDownward() {
107+
if (this.currentRowIndex === this.gridWindow.end) {
108+
this.shiftRowsBy(1);
109+
}
110+
}
111+
112+
private shiftRowsBy(offset: number) {
113+
this.gridWindow.start = this.gridWindow.start + offset;
114+
this.gridWindow.end = this.gridWindow.end + offset;
115+
}
116+
117+
private ensureRow() {
118+
if (this.currentRowIndex + 1 >= this.rows.length) {
119+
this.rows.push(new Row(this.header.length));
120+
}
121+
}
122+
123+
private init() {
124+
this.gridWindow = {pageSize: 10, start: 0, end: 10};
125+
this.currentColumn = this.rows[0].columns[0];
126+
this.currentRowIndex = 0;
127+
}
128+
}
129+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// #docregion
2+
export class KeyCodeService {
3+
4+
getNavigationKey(keyCode: number): any {
5+
return {
6+
up: keyCode === 38,
7+
down: keyCode === 40,
8+
right: keyCode === 39,
9+
left: keyCode === 37,
10+
tab: keyCode === 9
11+
};
12+
}
13+
}
14+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { bootstrap } from '@angular/platform-browser-dynamic';
2+
3+
import { AppComponent } from './app.component';
4+
5+
bootstrap(AppComponent);
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// #docregion
2+
import { Column } from './column';
3+
4+
export class Row {
5+
6+
columns: Array<Column>;
7+
8+
constructor(public columnCount: number) {
9+
this.columns = [];
10+
11+
for (let j = 0; j < this.columnCount; j++) {
12+
this.columns.push(new Column(j, this));
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)