Merge pull request #459 from dod-ccpo/show-sort-indicators
Show sort indicators when page loads
This commit is contained in:
commit
bdb0e827b0
54
js/components/__tests__/members_list.test.js
Normal file
54
js/components/__tests__/members_list.test.js
Normal 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])
|
||||
})
|
||||
})
|
59
js/components/__tests__/requests_list.test.js
Normal file
59
js/components/__tests__/requests_list.test.js
Normal 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'])
|
||||
})
|
||||
})
|
||||
})
|
@ -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;
|
||||
},
|
||||
},
|
||||
}
|
@ -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>'
|
||||
}
|
151
js/components/requests_list.js
Normal file
151
js/components/requests_list.js
Normal 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>'
|
||||
}
|
@ -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
|
||||
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user