project sort working

This commit is contained in:
Radek Stepan 2016-01-16 13:46:34 +01:00
parent 04f6fe00de
commit c35859fd4e
11 changed files with 376 additions and 15 deletions

View File

@ -28,6 +28,6 @@ build:
${MAKE} build-css
test:
${MOCHA} --compilers js:babel/register --ui exports --timeout 5000 --bail --reporter spec
${MOCHA} --compilers js:babel-register --ui exports --timeout 5000 --bail --reporter spec
.PHONY: test

View File

@ -28,12 +28,14 @@
"babel": "^6.3.26",
"babel-preset-es2015": "^6.3.13",
"babel-preset-react": "^6.3.13",
"babel-register": "^6.4.3",
"babelify": "^7.2.0",
"browserify": "^13.0.0",
"chai": "^3.4.1",
"coffeeify": "^2.0.1",
"less": "^2.5.3",
"mocha": "^2.3.4",
"proxyquire": "^1.7.3",
"superstatic": "^4.0.1",
"uglify-js": "^2.6.1",
"watch": "^0.17.1",

View File

@ -76,7 +76,7 @@ export default React.createClass({
// Show projects.
projects() {
document.title = 'Burnchart: GitHub Burndown Chart as a Service';
process.nextTick(() => actions.emit('projects.load'));
actions.emit('projects.load');
return <ProjectsPage />;
},

View File

@ -4,6 +4,8 @@ import cls from 'classnames';
import Format from '../mixins/Format.js';
import actions from '../actions/appActions.js';
import Icon from './Icon.jsx';
import Link from './Link.jsx';
@ -13,9 +15,8 @@ export default React.createClass({
mixins: [ Format ],
// TODO: implement
_onSort() {
actions.emit('projects.sort');
},
render() {

View File

@ -20,7 +20,7 @@ export default React.createClass({
let projects = this.state.projects;
if (projects.list.length) {
// Show a list of projects.
content = <div><Projects projects={projects} /></div>;
content = <Projects projects={projects} />;
} else {
content = <Hero />;
}

View File

@ -67,6 +67,11 @@ class AppStore extends Store {
this.set('system.loading', state);
}
// TODO: implement.
onSystemNotify() {
}
}
export default new AppStore();

View File

@ -18,7 +18,7 @@ class ProjectsStore extends Store {
// Initial payload.
constructor() {
// Init the projects.
let list = lscache.get('projects') || [ ];
let list = lscache.get('projects') || [];
super({
// A stack of projects.
@ -27,7 +27,7 @@ class ProjectsStore extends Store {
'index': [],
// The default sort order.
'sortBy': 'priority',
// Sort functions.
// Sort functions to toggle through.
'sortFns': [ 'progress', 'priority', 'name' ]
});
@ -53,7 +53,7 @@ class ProjectsStore extends Store {
// Reset our index and re-sort.
this.on('sortBy', () => {
this.set('index', null);
this.set('index', []);
// Run the sort again.
this.sort();
});
@ -123,6 +123,16 @@ class ProjectsStore extends Store {
}
}
// Cycle through projects sort order.
onProjectsSort() {
let { sortBy, sortFns } = this.get();
let idx = 1 + sortFns.indexOf(sortBy);
if (idx === sortFns.length) idx = 0;
this.set('sortBy', sortFns[idx]);
}
// Demonstration projects.
onProjectsDemo() {
this.set({
@ -153,9 +163,11 @@ class ProjectsStore extends Store {
let defaults = (arr, hash) => {
for (let item of arr) {
for (let key in hash) {
if (!opa.has(item, key)) {
opa.set(item, key, hash[key]);
}
}
}
};
// The actual fn selection.
@ -248,17 +260,18 @@ class ProjectsStore extends Store {
// Get the existing index.
let index = this.get('index');
// Do one.
// Index one milestone in an already sorted index.
if (ref) {
idx = sortedIndex(index, data, this.comparator());
index.splice(idx, 0, ref);
// Do all.
// Sort them all.
} else {
let list = this.get('list');
for (let i = 0; i < list.length; i++) {
let p = list[i];
// TODO: need to show projects that failed too...
if (p.milestones == null) continue;
// Walk the milestones.
for (let j = 0; j < p.milestones.length; j++) {
let m = p.milestones[j];
// Run a comparator here inserting into index.

View File

@ -25,6 +25,7 @@ a {
text-decoration: none;
color: #aaafbf;
cursor: pointer;
.user-select(none);
}
h1, h2, h3, p {
@ -174,7 +175,6 @@ ul {
a {
color: #e0808d;
font-weight: bold;
.user-select(none);
&.active, &:hover {
color: #fff;

208
test/projects.js Normal file
View File

@ -0,0 +1,208 @@
import { assert } from 'chai';
import projects from '../src/js/stores/projectsStore.js';
export default {
'projects - initializes empty': (done) => {
assert.deepEqual(projects.get('list'), []);
done();
},
'projects - sorts on new milestones': (done) => {
projects.set({ 'list': [], 'index': [] });
let project = {
'owner': 'radekstepan',
'name': 'burnchart'
};
let milestone = {
'title': '1.0.0',
'stats': {}
};
projects.push('list', project);
projects.addMilestone(project, milestone);
assert.deepEqual(projects.get('index'), [[0, 0]]);
done();
},
'projects - sort by progress': (done) => {
projects.set({ 'list': [], 'index': [], 'sortBy': 'progress' });
let project = {
'owner': 'radekstepan',
'name': 'burnchart'
};
let milestone1 = {
'title': '1.0.0',
'stats': {
'progress': {
'points': 5
}
}
};
let milestone2 = {
'title': '2.0.0',
'stats': {
'progress': {
'points': 7
}
}
};
projects.push('list', project);
projects.addMilestone(project, milestone1);
projects.addMilestone(project, milestone2);
assert.deepEqual(projects.get('index'), [[0, 1], [0, 0]]);
done();
},
'projects - sort by priority': (done) => {
projects.set({ 'list': [], 'index': [], 'sortBy': 'priority' });
let project = {
'owner': 'radekstepan',
'name': 'burnchart'
};
let milestone1 = {
'title': '1.0.0',
'stats': {
'progress': {
'points': 2,
'time': 1
},
'days': 2
}
};
let milestone2 = {
'title': '2.0.0',
'stats': {
'progress': {
'points': 2,
'time': 1
},
'days': 3
}
};
let milestone3 = {
'title': '3.0.0',
'stats': {
'progress': {
'points': 1,
'time': 2
},
'days': 4
}
};
projects.push('list', project);
projects.addMilestone(project, milestone1);
projects.addMilestone(project, milestone2);
projects.addMilestone(project, milestone3);
assert.deepEqual(projects.get('index'), [[0, 2], [0, 0], [0, 1]]);
done();
},
'projects - sort by priority defaults': (done) => {
projects.set({ 'list': [], 'index': [], 'sortBy': 'priority' });
let project = {
'owner': 'radekstepan',
'name': 'burnchart'
};
let milestone1 = {
'title': '1.0.0',
'stats': {
'progress': {
'points': 3
}
}
};
let milestone2 = {
'title': '2.0.0',
'stats': {
'progress': {
'points': 2
}
}
};
let milestone3 = {
'title': '3.0.0',
'stats': {
'progress': {
'points': 1
}
}
};
projects.push('list', project);
projects.addMilestone(project, milestone1);
projects.addMilestone(project, milestone2);
projects.addMilestone(project, milestone3);
assert.deepEqual(projects.get('index'), [[0, 2], [0, 1], [0, 0]]);
done();
},
'projects - sort by name': (done) => {
projects.set({ 'list': [], 'index': [], 'sortBy': 'name' });
let project = {
'owner': 'radekstepan',
'name': 'burnchart'
};
let milestone1 = {
'title': 'B',
'stats': {}
};
let milestone2 = {
'title': 'A',
'stats': {}
};
projects.push('list', project);
projects.addMilestone(project, milestone1);
projects.addMilestone(project, milestone2);
assert.deepEqual(projects.get('index'), [[0, 1], [0, 0]]);
done();
},
'projects - sort by name semver': (done) => {
projects.set({ 'list': [], 'index': [], 'sortBy': 'name' });
let project = {
'owner': 'radekstepan',
'name': 'burnchart'
};
let milestone1 = {
'title': '1.2.5',
'stats': {}
};
let milestone2 = {
'title': '1.1.x',
'stats': {}
};
let milestone3 = {
'title': '1.1.7',
'stats': {}
};
projects.push('list', project);
projects.addMilestone(project, milestone1);
projects.addMilestone(project, milestone2);
projects.addMilestone(project, milestone3);
assert.deepEqual(projects.get('index'), [[0, 2], [0, 1], [0, 0]]);
done();
}
};

132
test/stats.js Normal file
View File

@ -0,0 +1,132 @@
import { assert } from 'chai';
import { noCallThru } from 'proxyquire';
import path from 'path';
import moment from 'moment';
let proxy = noCallThru();
import stats from '../src/js/modules/stats.js';
export default {
'stats - is milestone empty, on time and overdue? no due date': (done) => {
let milestone = {
'issues': {
'open': {
'size': 0
},
'closed': {
'size': 0
}
}
};
let { isEmpty, isOverdue, isOnTime } = stats(milestone);
assert.isTrue(isEmpty);
assert.isFalse(isOverdue);
assert.isTrue(isOnTime);
done();
},
'stats - is milestone done?': (done) => {
let milestone = {
'issues': {
'open': {
'size': 0
},
'closed': {
'size': 5
}
}
};
let { isDone } = stats(milestone);
assert.isTrue(isDone);
done();
},
'stats - is milestone overdue? has due date, yes': (done) => {
let milestone = {
'created_at': '2011-04-02T00:00:00.000Z',
'due_on': '2011-04-03T00:00:00.000Z',
'issues': {
'open': {
'size': 0
},
'closed': {
'size': 0
}
}
};
let { isOverdue } = stats(milestone);
assert.isTrue(isOverdue);
done();
},
'stats - is milestone on time? has due date, yes': (done) => {
let now = moment.utc();
let milestone = {
'created_at': now.subtract(1, 'week').toISOString(),
'due_on': now.add(1, 'month').toISOString(),
'issues': {
'open': {
'size': 1
},
'closed': {
'size': 1
}
}
};
let { isOnTime } = stats(milestone);
assert.isTrue(isOnTime);
done();
},
'stats - is milestone on time? has due date, no': (done) => {
let now = moment.utc();
let milestone = {
'created_at': now.subtract(2, 'week').toISOString(),
'due_on': now.add(1, 'day').toISOString(),
'issues': {
'open': {
'size': 2
},
'closed': {
'size': 2
}
}
};
let { isOnTime } = stats(milestone);
assert.isFalse(isOnTime);
done();
},
'stats - is milestone on time? has due date, all issues closed': (done) => {
let now = moment.utc();
let milestone = {
'created_at': now.subtract(2, 'week').toISOString(),
'due_on': now.subtract(1, 'week').toISOString(),
'issues': {
'open': {
'size': 0
},
'closed': {
'size': 5
}
}
};
let { isOnTime } = stats(milestone);
assert.isTrue(isOnTime);
done();
}
};