Skip to content

Commit

Permalink
feat(table-sortable): columns can be sortable or unsortable
Browse files Browse the repository at this point in the history
[#80914122]

Signed-off-by: Geoff Pleiss <gpleiss@pivotal.io>
  • Loading branch information
bebepeng authored and stubbornella committed Dec 4, 2014
1 parent e5d9a2d commit 5d5151a
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 51 deletions.
9 changes: 6 additions & 3 deletions src/pivotal-ui/components/tables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -345,15 +345,18 @@ $(function() {
var columns = [
{
name: 'name',
title: 'Name'
title: 'Name',
sortable: true
},
{
name: 'instances',
title: 'Instances'
title: 'Instances',
sortable: true
},
{
name: 'cpu',
title: 'CPU'
title: 'CPU',
sortable: true
},
{
name: 'synergy',
Expand Down
22 changes: 13 additions & 9 deletions src/pivotal-ui/javascripts/table-sortable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,26 @@ var _ = require('lodash');

var TableHeader = React.createClass({
handleClick: function handleClick(e) {
this.props.onTableHeaderClick(this);
if (this.props.sortable) {
this.props.onSortableTableHeaderClick(this);
}
},

render: function render() {
var sortClass;

if (this.props.sortState.column !== this.props.key) {
sortClass = 'sorted-none';
} else if (this.props.sortState.order === 'asc') {
sortClass = 'sorted-asc';
} else {
sortClass = 'sorted-desc';
if (this.props.sortable) {
if (this.props.sortState.column !== this.props.key) {
sortClass = 'sortable sorted-none';
} else if (this.props.sortState.order === 'asc') {
sortClass = 'sortable sorted-asc';
} else {
sortClass = 'sortable sorted-desc';
}
}

return (
<th className={'sortable ' + sortClass} onClick={this.handleClick}>
<th className={sortClass} onClick={this.handleClick}>
{this.props.children}
</th>
);
Expand Down Expand Up @@ -86,7 +90,7 @@ var TableSortable = module.exports = React.createClass({
render: function render() {
var headings = _.map(this.props.columns, function(column) {
return (
<TableHeader key={column.name} sortState={this.state.sort} onTableHeaderClick={this.sortData}>
<TableHeader key={column.name} sortable={column.sortable} sortState={this.state.sort} onSortableTableHeaderClick={this.sortData}>
{column.title}
</TableHeader>
);
Expand Down
123 changes: 84 additions & 39 deletions test/spec/javascripts/table_sortable_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,35 @@ describe('TableSortable', function() {
this.columns = [
{
name: 'title',
title: 'Title'
title: 'Title',
sortable: true
},
{
name: 'instances',
title: 'Instances'
title: 'Instances',
sortable: true
},
{
name: 'unsortable',
title: 'Unsortable',
sortable: false
}
];
this.data = [
{
instances: '1',
title: 'foo'
title: 'foo',
unsortable: '14'
},
{
instances: '3',
title: 'sup'
title: 'sup',
unsortable: '22'
},
{
title: 'yee',
instances: '2'
instances: '2',
unsortable: '1'
}
];

Expand All @@ -42,68 +52,103 @@ describe('TableSortable', function() {
}),
this.node
);

this.$table = $('#container table.table-sortable');
});

afterEach(function() {
React.unmountComponentAtNode(this.node);
});

it('adds the class "sortable" on all sortable columns', function() {
expect(this.$table.find('th:contains("Title")')).toHaveClass('sortable');
expect(this.$table.find('th:contains("Instances")')).toHaveClass('sortable');
expect(this.$table.find('th:contains("Unsortable")')).not.toHaveClass('sortable');
});

it('sorts table rows by the first column in ascending order by default', function() {
expect($('th:contains("Title")')).toHaveClass('sorted-asc');
expect($('td').eq(0)).toContainText('foo');
expect($('td').eq(1)).toContainText('1');
expect($('td').eq(2)).toContainText('sup');
expect($('td').eq(3)).toContainText('3');
expect($('td').eq(4)).toContainText('yee');
expect($('td').eq(5)).toContainText('2');
expect(this.$table.find('th:contains("Title")')).toHaveClass('sorted-asc');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(0)).toContainText('foo');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(0)).toContainText('sup');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(0)).toContainText('yee');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(1)).toContainText('1');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(1)).toContainText('3');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(1)).toContainText('2');
});

describe('clicking on the already asc-sorted column', function() {
beforeEach(function() {
TestUtils.Simulate.click($("th:contains('Title')").get(0));
TestUtils.Simulate.click(this.$table.find('th:contains("Title")').get(0));
});

it('reverses the sort order', function() {
expect($('th:contains("Title")')).toHaveClass('sorted-desc');
expect($('td').eq(0)).toContainText('yee');
expect($('td').eq(1)).toContainText('2');
expect($('td').eq(2)).toContainText('sup');
expect($('td').eq(3)).toContainText('3');
expect($('td').eq(4)).toContainText('foo');
expect($('td').eq(5)).toContainText('1');
expect(this.$table.find('th:contains("Title")')).toHaveClass('sorted-desc');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(0)).toContainText('yee');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(0)).toContainText('sup');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(0)).toContainText('foo');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(1)).toContainText('2');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(1)).toContainText('3');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(1)).toContainText('1');
});

describe('clicking on the already desc-sorted column', function() {
beforeEach(function() {
TestUtils.Simulate.click($("th:contains('Title')").get(0));
TestUtils.Simulate.click(this.$table.find('th:contains("Title")').get(0));
});

it('reverses the sort order', function() {
expect($('th:contains("Title")')).toHaveClass('sorted-asc');
expect($('td').eq(0)).toContainText('foo');
expect($('td').eq(1)).toContainText('1');
expect($('td').eq(2)).toContainText('sup');
expect($('td').eq(3)).toContainText('3');
expect($('td').eq(4)).toContainText('yee');
expect($('td').eq(5)).toContainText('2');
expect(this.$table.find('th:contains("Title")')).toHaveClass('sorted-asc');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(0)).toContainText('foo');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(0)).toContainText('sup');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(0)).toContainText('yee');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(1)).toContainText('1');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(1)).toContainText('3');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(1)).toContainText('2');
});
});
});

describe('clicking on the instances column', function() {
describe('clicking on a sortable column', function() {
beforeEach(function() {
TestUtils.Simulate.click($("th:contains('Instances')").get(0));
TestUtils.Simulate.click(this.$table.find('th:contains("Instances")').get(0));
});

it('sorts table rows by asc-instances', function() {
expect($('th:contains("Instances")')).toHaveClass('sorted-asc');
expect($('th:contains("Title")')).not.toHaveClass('sorted-asc');
expect($('td').eq(0)).toContainText('foo');
expect($('td').eq(1)).toContainText('1');
expect($('td').eq(2)).toContainText('yee');
expect($('td').eq(3)).toContainText('2');
expect($('td').eq(4)).toContainText('sup');
expect($('td').eq(5)).toContainText('3');
it('sorts table rows by that column', function() {
expect(this.$table.find('th:contains("Instances")')).toHaveClass('sorted-asc');
expect(this.$table.find('th:contains("Title")')).not.toHaveClass('sorted-asc');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(0)).toContainText('foo');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(0)).toContainText('yee');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(0)).toContainText('sup');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(1)).toContainText('1');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(1)).toContainText('2');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(1)).toContainText('3');
});
});

describe('clicking on a non-sortable column', function() {
beforeEach(function() {
TestUtils.Simulate.click(this.$table.find('th:contains("Unsortable")').get(0));
});

it('does not change the sort', function() {
expect(this.$table.find('th:contains("Unsortable")')).not.toHaveClass('sorted-asc');
expect(this.$table.find('th:contains("Title")')).toHaveClass('sorted-asc');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(0)).toContainText('foo');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(0)).toContainText('sup');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(0)).toContainText('yee');

expect(this.$table.find('tbody tr:nth-of-type(1) > td').eq(1)).toContainText('1');
expect(this.$table.find('tbody tr:nth-of-type(2) > td').eq(1)).toContainText('3');
expect(this.$table.find('tbody tr:nth-of-type(3) > td').eq(1)).toContainText('2');
});
});
});
Expand Down

0 comments on commit 5d5151a

Please sign in to comment.