-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fb0845d
commit 2d0fc4b
Showing
35 changed files
with
1,598 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
Table.$inject = ['lodash']; | ||
|
||
/** | ||
* @ngdoc directive | ||
* @module superdesk.apps.analytics.charts | ||
* @name sdaTable | ||
* @requires lodash | ||
* @description A directive that renders a sortable and clickable html table | ||
*/ | ||
export function Table(_) { | ||
return { | ||
scope: { | ||
title: '=', | ||
subtitle: '=', | ||
headers: '=', | ||
rows: '=', | ||
page: '=', | ||
onCellClicked: '=', | ||
}, | ||
replace: true, | ||
template: require('../views/table.html'), | ||
link: function(scope) { | ||
/** | ||
* @ngdoc method | ||
* @name sdaTable#onHeaderClicked | ||
* @param {Object} header - The header column that was clicked | ||
* @description Determines the sort filter based on the header data | ||
*/ | ||
scope.onHeaderClicked = (header) => { | ||
// If the header entry does not have a field attribute | ||
// then sorting by this column is disabled | ||
if (!header.field) { | ||
return; | ||
} | ||
|
||
let newOrder; | ||
|
||
if (_.get(scope, 'page.sort.field') === header.field) { | ||
// If the header.field is already sorted, then toggle the sort order | ||
if (_.get(scope, 'page.sort.order') === 'asc') { | ||
newOrder = 'desc'; | ||
} else { | ||
newOrder = 'asc'; | ||
} | ||
} else { | ||
// Otherwise set order to descending by default on first click | ||
newOrder = 'desc'; | ||
} | ||
|
||
scope.page = { | ||
...scope.page, | ||
no: 1, | ||
sort: { | ||
field: header.field, | ||
order: newOrder, | ||
}, | ||
}; | ||
}; | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export {Chart} from './Chart'; | ||
export {ChartContainer} from './ChartContainer'; | ||
export {Table} from './Table'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
describe('sda-table', () => { | ||
let $compile; | ||
let scope; | ||
let $rootScope; | ||
let data; | ||
let element; | ||
|
||
beforeEach(window.module('superdesk.core.notify')); | ||
beforeEach(window.module('superdesk.analytics.charts')); | ||
|
||
beforeEach(inject((_$compile_, _$rootScope_) => { | ||
$compile = _$compile_; | ||
$rootScope = _$rootScope_; | ||
|
||
element = null; | ||
|
||
data = { | ||
title: 'Test Table', | ||
subtitle: 'Fake Data', | ||
headers: [ | ||
{title: 'one', field: 'one'}, | ||
{title: 'two'}, | ||
{title: 'three', field: 'three'}, | ||
], | ||
rows: [ | ||
[{label: 1}, {label: 2}, {label: 3, clickable: true, tooltip: 'View data', custom: {data: [3]}}], | ||
[{label: 4}, {label: 5}, {label: 6, clickable: true, tooltip: 'View data'}], | ||
[{label: 7}, {label: 8}, {label: 9, clickable: true, tooltip: 'View data'}], | ||
], | ||
page: { | ||
no: 1, | ||
max: 5, | ||
sort: { | ||
field: 'one', | ||
order: 'desc', | ||
}, | ||
}, | ||
onCellClicked: jasmine.createSpy(), | ||
}; | ||
})); | ||
|
||
const setElement = () => { | ||
scope = $rootScope.$new(); | ||
Object.keys(data).forEach((key) => { | ||
scope[key] = data[key]; | ||
}); | ||
|
||
const template = angular.element(`<div><div | ||
sda-table | ||
data-title="title" | ||
data-subtitle="subtitle" | ||
data-headers="headers" | ||
data-rows="rows" | ||
data-page="page" | ||
data-on-cell-clicked="onCellClicked" | ||
</div>`); | ||
|
||
element = $compile(template)(scope); | ||
|
||
$rootScope.$digest(); | ||
}; | ||
|
||
const getHeader = (index) => $( | ||
element.find('thead') | ||
.first() | ||
.find('tr') | ||
.first() | ||
.find('th') | ||
.get(index) | ||
); | ||
|
||
const getBodyRow = (index) => $( | ||
element.find('tbody') | ||
.first() | ||
.find('tr') | ||
.get(index) | ||
); | ||
|
||
const getCell = (row, column) => $( | ||
getBodyRow(row) | ||
.find('td') | ||
.get(column) | ||
); | ||
|
||
const stripWhitespace = (elem) => ( | ||
elem.text().replace(/\s+/g, '') | ||
); | ||
|
||
const trim = (elem) => ( | ||
elem.text().trim() | ||
); | ||
|
||
it('renders the table', () => { | ||
setElement(); | ||
|
||
// Test titles | ||
expect(trim(element.find('.sd-chart__table-header-title'))).toBe('Test Table'); | ||
expect(trim(element.find('.sd-chart__table-header-subtitle'))).toBe('Fake Data'); | ||
|
||
// Test header | ||
expect(trim(getHeader(0))).toBe('one'); | ||
expect(trim(getHeader(1))).toBe('two'); | ||
expect(trim(getHeader(2))).toBe('three'); | ||
|
||
// Test size of the table | ||
expect(element.find('table').length).toBe(1); | ||
expect(element.find('tbody') | ||
.first() | ||
.find('tr') | ||
.length | ||
).toBe(3); | ||
|
||
// Test data of each row | ||
expect(getBodyRow(0).find('td').length).toBe(3); | ||
expect(stripWhitespace(getBodyRow(0))).toBe('123'); | ||
expect(stripWhitespace(getBodyRow(1))).toBe('456'); | ||
expect(stripWhitespace(getBodyRow(2))).toBe('789'); | ||
|
||
// Renders the pagination directive if page.max > 1 | ||
scope.page = {...data.page, max: 1}; | ||
scope.$digest(); | ||
expect(element.find('.pagination-box').length).toBe(0); | ||
|
||
scope.page = {...data.page, max: 5}; | ||
scope.$digest(); | ||
expect(element.find('.pagination-box').length).toBe(1); | ||
|
||
// Renders panel-info when no rows are to be rendered | ||
expect(element.find('.panel-info').length).toBe(0); | ||
scope.rows = []; | ||
scope.$digest(); | ||
expect(element.find('.panel-info').length).toBe(1); | ||
}); | ||
|
||
it('executes callback on cell clicked', () => { | ||
setElement(); | ||
|
||
// Test onCellClicked on cells with clickable=true | ||
getCell(0, 0).click(); | ||
getCell(0, 1).click(); | ||
getCell(0, 2).click(); | ||
expect(data.onCellClicked.calls.count()).toBe(1); | ||
expect(data.onCellClicked.calls.mostRecent().args).toEqual( | ||
[{label: 3, clickable: true, tooltip: 'View data', custom: {data: [3]}}] | ||
); | ||
|
||
getCell(1, 2).click(); | ||
expect(data.onCellClicked.calls.count()).toBe(2); | ||
expect(data.onCellClicked.calls.mostRecent().args).toEqual( | ||
[{label: 6, clickable: true, tooltip: 'View data'}] | ||
); | ||
|
||
getCell(2, 2).click(); | ||
expect(data.onCellClicked.calls.count()).toBe(3); | ||
expect(data.onCellClicked.calls.mostRecent().args).toEqual( | ||
[{label: 9, clickable: true, tooltip: 'View data'}] | ||
); | ||
}); | ||
|
||
it('sorts on header click', () => { | ||
setElement(); | ||
|
||
// Test initial values | ||
expect(scope.page.sort).toEqual({field: 'one', order: 'desc'}); | ||
expect(getHeader(0).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(0).find('.icon-chevron-down-thin').length).toBe(1); | ||
expect(getHeader(1).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-down-thin').length).toBe(0); | ||
|
||
// Clicking on already selected header toggles the order | ||
getHeader(0).click(); | ||
expect(scope.page.sort).toEqual({field: 'one', order: 'asc'}); | ||
expect(getHeader(0).find('.icon-chevron-up-thin').length).toBe(1); | ||
expect(getHeader(0).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-down-thin').length).toBe(0); | ||
|
||
// Test clicking on non-sortable header field | ||
// page value stays the same | ||
getHeader(1).click(); | ||
expect(scope.page.sort).toEqual({field: 'one', order: 'asc'}); | ||
expect(getHeader(0).find('.icon-chevron-up-thin').length).toBe(1); | ||
expect(getHeader(0).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-down-thin').length).toBe(0); | ||
|
||
// Clicking on a non-selected header defaults to descending order | ||
getHeader(2).click(); | ||
expect(scope.page.sort).toEqual({field: 'three', order: 'desc'}); | ||
expect(getHeader(0).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(0).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-down-thin').length).toBe(1); | ||
|
||
// Again clicking on already selected header toggles the order | ||
getHeader(2).click(); | ||
expect(scope.page.sort).toEqual({field: 'three', order: 'asc'}); | ||
expect(getHeader(0).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(0).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-up-thin').length).toBe(0); | ||
expect(getHeader(1).find('.icon-chevron-down-thin').length).toBe(0); | ||
expect(getHeader(2).find('.icon-chevron-up-thin').length).toBe(1); | ||
expect(getHeader(2).find('.icon-chevron-down-thin').length).toBe(0); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.