Merge pull request #459 from dod-ccpo/show-sort-indicators

Show sort indicators when page loads
This commit is contained in:
patricksmithdds 2018-11-28 16:31:16 -05:00 committed by GitHub
commit bdb0e827b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 273 additions and 153 deletions

View File

@ -0,0 +1,54 @@
import { shallowMount } from '@vue/test-utils'
import MembersList from '../members_list'
describe('MembersList', () => {
const members = [{
name: 'Luke Skywalker',
num_env: 2,
status: 'active',
role: 'developer'
}, {
name: 'Chewie',
num_env: 3,
status: 'pending',
role: 'admin'
}]
const role_choices = [
{ display_name: 'Developer', name: 'developer' },
{ display_name: 'Admin', name: 'admin' },
]
const status_choices = [
{ display_name: 'Active', name: 'active' },
{ display_name: 'Pending', name: 'pending' },
]
const createWrapper = () => shallowMount(MembersList, {
propsData: {
members, role_choices, status_choices
}
})
it('should sort by name by default', () => {
const wrapper = createWrapper()
const listedMembers = wrapper.vm.searchedList
const memberNames = listedMembers.map(member => member.name)
expect(memberNames).toEqual(['Chewie', 'Luke Skywalker'])
})
it('should reverse sort by name when updated with Name', () => {
const wrapper = createWrapper()
wrapper.vm.updateSort('Name')
const listedMembers = wrapper.vm.searchedList
const memberNames = listedMembers.map(member => member.name)
expect(memberNames).toEqual(['Luke Skywalker', 'Chewie'])
})
it('should sort by number of environments when environments selected', () => {
const wrapper = createWrapper()
wrapper.vm.updateSort('Environments')
const listedMembers = wrapper.vm.searchedList
const memberEnvs = listedMembers.map(member => member.num_env)
expect(memberEnvs).toEqual([2, 3])
})
})

View File

@ -0,0 +1,59 @@
import { shallowMount } from '@vue/test-utils'
import RequestsList from '../requests_list'
describe('RequestsList', () => {
describe('isExtended', () => {
it('should disallow sorting if not extended', () => {
const wrapper = shallowMount(RequestsList, { propsData: { isExtended: false } })
expect(wrapper.vm.sort.columnName).toEqual('')
wrapper.vm.updateSortValue('full_name')
expect(wrapper.vm.sort.columnName).toEqual('')
})
it('should allow sorting when in extended mode', () => {
const wrapper = shallowMount(RequestsList, { propsData: { isExtended: true } })
expect(wrapper.vm.sort.columnName).toEqual('last_submission_timestamp')
wrapper.vm.updateSortValue('full_name')
expect(wrapper.vm.sort.columnName).toEqual('full_name')
})
})
describe('sorting', () => {
const requests = [{
name: 'X Wing',
last_edited_timestamp: 'Mon, 2 Jan 2017 12:34:56 GMT',
last_submission_timestamp: 'Mon, 2 Jan 2017 12:34:56 GMT',
full_name: 'Luke Skywalker',
annual_usage: '80000',
status: 'Approved',
dod_component: 'Rebels'
}, {
name: 'TIE Fighter',
last_edited_timestamp: 'Mon, 12 Nov 2018 12:34:56 GMT',
last_submission_timestamp: 'Mon, 12 Nov 2018 12:34:56 GMT',
full_name: 'Darth Vader',
annual_usage: '999999',
status: 'Approved',
dod_component: 'Empire'
}]
const mountWrapper = () => shallowMount(RequestsList, { propsData: { requests, isExtended: true } })
it('should default to sorting by submission recency', () => {
const wrapper = mountWrapper()
const displayedRequests = wrapper.vm.filteredRequests
const requestNames = displayedRequests.map(req => req.name)
expect(requestNames).toEqual(['TIE Fighter', 'X Wing'])
})
it('should reverse sort by submission time when selected', () => {
const wrapper = mountWrapper()
wrapper.vm.updateSortValue('last_submission_timestamp')
const displayedRequests = wrapper.vm.filteredRequests
const requestNames = displayedRequests.map(req => req.name)
expect(requestNames).toEqual(['X Wing', 'TIE Fighter'])
})
})
})

View File

@ -1,148 +0,0 @@
import LocalDatetime from '../../components/local_datetime'
import { formatDollars } from '../../lib/dollars'
import { parse } from 'date-fns'
import { compose, partial, indexBy, prop, sortBy, reverse, pipe } from 'ramda'
export default {
name: 'requests-list',
components: {
LocalDatetime,
},
props: {
requests: {
type: Array,
default: [],
},
isExtended: {
type: Boolean,
default: false,
},
statuses: {
type: Array,
default: [],
},
dodComponents: {
type: Array,
default: [],
}
},
data: function () {
const defaultSort = (sort, requests) => sortBy(prop(sort.columnName), requests)
const dateSort = (sort, requests) => {
const parseDate = compose(partial(parse), prop(sort.columnName))
return sortBy(parseDate, requests)
}
const columnList = [
{
displayName: 'JEDI Cloud Request Name',
attr: 'name',
sortFunc: defaultSort,
},
{
displayName: 'Date Request Submitted',
attr: 'last_submission_timestamp',
sortFunc: dateSort,
},
{
displayName: 'Date Request Last Edited',
attr: 'last_edited_timestamp',
extendedOnly: true,
sortFunc: dateSort,
},
{
displayName: 'Requester',
attr: 'full_name',
extendedOnly: true,
sortFunc: defaultSort,
},
{
displayName: 'Projected Annual Usage ($)',
attr: 'annual_usage',
sortFunc: defaultSort,
},
{
displayName: 'Request Status',
attr: 'status',
sortFunc: defaultSort,
},
{
displayName: 'DOD Component',
attr: 'dod_component',
extendedOnly: true,
sortFunc: defaultSort,
},
]
return {
searchValue: '',
statusValue: '',
dodComponentValue: '',
sort: {
columnName: '',
isAscending: true
},
columns: indexBy(prop('attr'), columnList),
}
},
computed: {
filteredRequests: function () {
return pipe(
partial(this.applySearch, [this.searchValue]),
partial(this.applyFilters, [this.statusValue, this.dodComponentValue]),
partial(this.applySort, [this.sort]),
)(this.requests)
}
},
methods: {
getColumns: function() {
return Object.values(this.columns)
.filter((column) => !column.extendedOnly || this.isExtended)
},
applySearch: (query, requests) => {
return requests.filter(
(request) => query !== '' ?
request.name.toLowerCase().includes(query.toLowerCase()) :
true
)
},
applyFilters: (status, dodComponent, requests) => {
return requests.filter(
(request) => status !== '' ?
request.status === status :
true
).filter(
(request) => dodComponent !== '' ?
request.dod_component === dodComponent :
true
)
},
applySort: function(sort, requests) {
if (sort.columnName === '') {
return requests
} else {
const { sortFunc } = this.columns[sort.columnName]
const sorted = sortFunc(sort, requests)
return sort.isAscending ?
sorted :
reverse(sorted)
}
},
dollars: (value) => formatDollars(value, false),
updateSortValue: function(columnName) {
if (!this.isExtended) { return }
// toggle ascending / descending if column is clicked twice
if (columnName === this.sort.columnName) {
this.sort.isAscending = !this.sort.isAscending
}
this.sort.columnName = columnName;
},
},
}

View File

@ -90,6 +90,8 @@ export default {
},
]
const defaultSortColumn = 'Name'
return {
searchValue: '',
status: '',
@ -97,7 +99,7 @@ export default {
role: '',
rolesByDisplayName: indexBy(prop('display_name'), this.role_choices),
sortInfo: {
columnName: '',
columnName: defaultSortColumn,
isAscending: true,
columns: indexBy(prop('displayName'), columns)
},
@ -127,5 +129,7 @@ export default {
getColumns: function() {
return Object.values(this.sortInfo.columns)
}
}
},
template: '<div></div>'
}

View File

@ -0,0 +1,151 @@
import LocalDatetime from '../components/local_datetime'
import { formatDollars } from '../lib/dollars'
import { parse } from 'date-fns'
import { compose, partial, indexBy, prop, sortBy, reverse, pipe } from 'ramda'
export default {
name: 'requests-list',
components: {
LocalDatetime,
},
props: {
requests: {
type: Array,
default: () => [],
},
isExtended: {
type: Boolean,
default: false,
},
statuses: {
type: Array,
default: () => [],
},
dodComponents: {
type: Array,
default: () => [],
}
},
data: function () {
const defaultSort = (sort, requests) => sortBy(prop(sort.columnName), requests)
const dateSort = (sort, requests) => {
const parseDate = compose(partial(parse), prop(sort.columnName))
return sortBy(parseDate, requests)
}
const columnList = [
{
displayName: 'JEDI Cloud Request Name',
attr: 'name',
sortFunc: defaultSort,
},
{
displayName: 'Date Request Submitted',
attr: 'last_submission_timestamp',
sortFunc: dateSort,
},
{
displayName: 'Date Request Last Edited',
attr: 'last_edited_timestamp',
extendedOnly: true,
sortFunc: dateSort,
},
{
displayName: 'Requester',
attr: 'full_name',
extendedOnly: true,
sortFunc: defaultSort,
},
{
displayName: 'Projected Annual Usage ($)',
attr: 'annual_usage',
sortFunc: defaultSort,
},
{
displayName: 'Request Status',
attr: 'status',
sortFunc: defaultSort,
},
{
displayName: 'DOD Component',
attr: 'dod_component',
extendedOnly: true,
sortFunc: defaultSort,
},
]
const defaultSortColumn = this.isExtended ? 'last_submission_timestamp' : ''
return {
searchValue: '',
statusValue: '',
dodComponentValue: '',
sort: {
columnName: defaultSortColumn,
isAscending: false
},
columns: indexBy(prop('attr'), columnList),
}
},
computed: {
filteredRequests: function () {
return pipe(
partial(this.applySearch, [this.searchValue]),
partial(this.applyFilters, [this.statusValue, this.dodComponentValue]),
partial(this.applySort, [this.sort]),
)(this.requests)
}
},
methods: {
getColumns: function() {
return Object.values(this.columns)
.filter((column) => !column.extendedOnly || this.isExtended)
},
applySearch: (query, requests) => {
return requests.filter(
(request) => query !== '' ?
request.name.toLowerCase().includes(query.toLowerCase()) :
true
)
},
applyFilters: (status, dodComponent, requests) => {
return requests.filter(
(request) => status !== '' ?
request.status === status :
true
).filter(
(request) => dodComponent !== '' ?
request.dod_component === dodComponent :
true
)
},
applySort: function(sort, requests) {
if (sort.columnName === '') {
return requests
} else {
const { sortFunc } = this.columns[sort.columnName]
const sorted = sortFunc(sort, requests)
return sort.isAscending ?
sorted :
reverse(sorted)
}
},
dollars: (value) => formatDollars(value, false),
updateSortValue: function(columnName) {
if (!this.isExtended) { return }
// toggle ascending / descending if column is clicked twice
if (columnName === this.sort.columnName) {
this.sort.isAscending = !this.sort.isAscending
}
this.sort.columnName = columnName;
},
},
template: '<div></div>'
}

View File

@ -20,9 +20,9 @@ import selector from './components/selector'
import BudgetChart from './components/charts/budget_chart'
import SpendTable from './components/tables/spend_table'
import CcpoApproval from './components/forms/ccpo_approval'
import MembersList from './components/forms/members_list'
import MembersList from './components/members_list'
import LocalDatetime from './components/local_datetime'
import RequestsList from './components/forms/requests_list'
import RequestsList from './components/requests_list'
import ConfirmationPopover from './components/confirmation_popover'
Vue.config.productionTip = false

View File

@ -8,7 +8,7 @@
"build": "parcel build js/index.js -d static/assets --public-url /static/assets -o index.js",
"test": "jest",
"test:coverage": "jest --coverage",
"test:watch": "jest --watch"
"test:watch": "jest --watch --no-cache"
},
"author": "",
"license": "MIT",