-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add date and dateTime filtering support to ClientSideDataService and …
…improve overall sorting and filtering of dates.
- Loading branch information
Showing
8 changed files
with
317 additions
and
15 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 |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
"datagridvue", | ||
"docsearch", | ||
"dragover", | ||
"falsey", | ||
"gapi", | ||
"keyvault", | ||
"navigatable", | ||
|
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,195 @@ | ||
import { expect, test, describe } from 'vitest' | ||
import { ClientSideDataService, StubDataService } from './DataService' | ||
import { SortType } from './Sort' | ||
import { DataType } from './DataGridVue' | ||
import { FilterOperator } from './Filter' | ||
|
||
interface TestDataItem { | ||
id: number | ||
name: string | ||
date: string | ||
} | ||
|
||
const TestDataItemOne = { id: 1, name: 'Test 1', date: '2024-01-13T12:33:00.000Z' } | ||
const TestDataItemTwo = { id: 2, name: 'Test 2', date: '2024-01-12T12:32:00.000Z' } | ||
const TestDataItemThree = { id: 2, name: 'Test 3', date: '2024-01-11T12:31:00.000Z' } | ||
const TestDataItemFour = { id: 4, name: 'Test 4', date: '2024-01-10T12:30:00.000Z' } | ||
const TestDataItemFive = { id: 5, name: 'Test 5', date: '2024-01-10T12:29:00.000Z' } | ||
|
||
const TestDataSet = [TestDataItemThree, TestDataItemTwo, TestDataItemFour, TestDataItemFive, TestDataItemOne] as TestDataItem[] | ||
|
||
describe('StubDataService', () => { | ||
test('getPageAsync', async () => { | ||
const pageData = await StubDataService.getPageAsync(1, 10, [], undefined) | ||
expect(pageData).toEqual({ | ||
totalItems: 0, | ||
dataItems: [], | ||
}) | ||
}) | ||
}) | ||
|
||
describe('ClientSideDataService', () => { | ||
test('getPageAsync | pages', async () => { | ||
const dataService = new ClientSideDataService([...TestDataSet]) | ||
const pageDataOne = await dataService.getPageAsync(1, 2, [], undefined) | ||
const pageDataTwo = await dataService.getPageAsync(2, 1, [], undefined) | ||
const pageDataThree = await dataService.getPageAsync(2, 3, [], undefined) | ||
expect(pageDataOne).toEqual({ | ||
totalItems: TestDataSet.length, | ||
dataItems: [TestDataItemThree, TestDataItemTwo], | ||
}) | ||
expect(pageDataTwo).toEqual({ | ||
totalItems: TestDataSet.length, | ||
dataItems: [TestDataItemTwo], | ||
}) | ||
expect(pageDataThree).toEqual({ | ||
totalItems: TestDataSet.length, | ||
dataItems: [TestDataItemFive, TestDataItemOne], | ||
}) | ||
}) | ||
|
||
test('getPageAsync | filters', async () => { | ||
const dataService = new ClientSideDataService([...TestDataSet]) | ||
|
||
const testCases = [ | ||
{ | ||
filter: { or: [{ fieldName: 'id', dataType: DataType.number, operator: FilterOperator.equals, value: '3' }], and: undefined }, | ||
pageNum: 1, | ||
pageSize: 2, | ||
expectedTotalItems: 0, | ||
expected: [], | ||
}, | ||
{ | ||
filter: { or: [{ fieldName: 'id', dataType: DataType.number, operator: FilterOperator.equals, value: '4' }], and: undefined }, | ||
pageNum: 1, | ||
pageSize: 2, | ||
expectedTotalItems: 1, | ||
expected: [TestDataItemFour], | ||
}, | ||
{ | ||
filter: { or: [{ fieldName: 'date', dataType: DataType.date, operator: FilterOperator.lessThan, value: '2024-01-11' }], and: undefined }, | ||
pageNum: 1, | ||
pageSize: 2, | ||
expectedTotalItems: 2, | ||
expected: [TestDataItemFour, TestDataItemFive], | ||
}, | ||
{ | ||
filter: { or: [{ fieldName: 'date', dataType: DataType.date, operator: FilterOperator.equals, value: '2024-01-11' }], and: undefined }, | ||
pageNum: 1, | ||
pageSize: 2, | ||
expectedTotalItems: 1, | ||
expected: [TestDataItemThree], | ||
}, | ||
{ | ||
filter: { or: [{ fieldName: 'date', dataType: DataType.dateTime, operator: FilterOperator.equals, value: '2024-01-11' }], and: undefined }, | ||
pageNum: 1, | ||
pageSize: 2, | ||
expectedTotalItems: 0, | ||
expected: [], | ||
}, | ||
{ | ||
filter: { | ||
or: [{ fieldName: 'date', dataType: DataType.dateTime, operator: FilterOperator.equals, value: '2024-01-10T12:30:00.000Z' }], | ||
and: undefined, | ||
}, | ||
pageNum: 1, | ||
pageSize: 2, | ||
expectedTotalItems: 1, | ||
expected: [TestDataItemFour], | ||
}, | ||
{ | ||
filter: { | ||
or: [{ fieldName: 'date', dataType: DataType.dateTime, operator: FilterOperator.equals, value: '2024-01-10T14:30:00.000+02:00' }], | ||
and: undefined, | ||
}, | ||
pageNum: 1, | ||
pageSize: 2, | ||
expectedTotalItems: 1, | ||
expected: [TestDataItemFour], | ||
}, | ||
] | ||
|
||
for (const testCase of testCases) { | ||
const pageData = await dataService.getPageAsync(testCase.pageNum, testCase.pageSize, [], testCase.filter) | ||
expect(pageData, JSON.stringify(testCase)).toEqual({ | ||
totalItems: testCase.expectedTotalItems, | ||
dataItems: testCase.expected, | ||
}) | ||
} | ||
}) | ||
|
||
test('getPageAsync | sorts', async () => { | ||
const dataService = new ClientSideDataService([...TestDataSet]) | ||
|
||
const testCases = [ | ||
{ | ||
sort: [{ fieldName: 'id', dataType: DataType.number, type: SortType.descending }], | ||
pageNum: 1, | ||
pageSize: 2, | ||
expected: [TestDataItemFive, TestDataItemFour], | ||
}, | ||
{ | ||
sort: [{ fieldName: 'id', dataType: DataType.number, type: SortType.descending }], | ||
pageNum: 2, | ||
pageSize: 2, | ||
expected: [TestDataItemThree, TestDataItemTwo], | ||
}, | ||
{ sort: [{ fieldName: 'id', dataType: DataType.number, type: SortType.descending }], pageNum: 3, pageSize: 2, expected: [TestDataItemOne] }, | ||
{ sort: [], pageNum: 3, pageSize: 1, expected: [TestDataItemFour] }, | ||
{ | ||
sort: [ | ||
{ fieldName: 'id', dataType: DataType.number, type: SortType.ascending }, | ||
{ fieldName: 'name', dataType: DataType.alphanumeric, type: SortType.descending }, | ||
], | ||
pageNum: 1, | ||
pageSize: 3, | ||
expected: [TestDataItemOne, TestDataItemThree, TestDataItemTwo], | ||
}, | ||
{ | ||
sort: [{ fieldName: 'date', dataType: DataType.date, type: SortType.ascending }], | ||
pageNum: 2, | ||
pageSize: 2, | ||
expected: [TestDataItemThree, TestDataItemTwo], | ||
}, | ||
{ | ||
sort: [{ fieldName: 'date', dataType: DataType.date, type: SortType.ascending }], | ||
pageNum: 1, | ||
pageSize: 2, | ||
expected: [TestDataItemFour, TestDataItemFive], | ||
}, | ||
{ | ||
sort: [{ fieldName: 'date', dataType: DataType.dateTime, type: SortType.ascending }], | ||
pageNum: 1, | ||
pageSize: 2, | ||
expected: [TestDataItemFive, TestDataItemFour], | ||
}, | ||
] | ||
|
||
for (const testCase of testCases) { | ||
const pageData = await dataService.getPageAsync(testCase.pageNum, testCase.pageSize, testCase.sort, undefined) | ||
expect(pageData, JSON.stringify(testCase)).toEqual({ | ||
totalItems: TestDataSet.length, | ||
dataItems: testCase.expected, | ||
}) | ||
} | ||
}) | ||
|
||
test('getPageAsync | filters and sorts', async () => { | ||
const dataService = new ClientSideDataService([...TestDataSet]) | ||
|
||
const filter = { or: [{ fieldName: 'id', dataType: DataType.number, operator: FilterOperator.greaterThan, value: '2' }], and: undefined } | ||
const sort = [ | ||
{ fieldName: 'date', dataType: DataType.dateTime, type: SortType.ascending }, | ||
{ fieldName: 'name', dataType: DataType.alphanumeric, type: SortType.descending }, | ||
] | ||
|
||
const pageData = await dataService.getPageAsync(1, 4, sort, filter) | ||
|
||
console.log(JSON.stringify(pageData, null, 2)) | ||
|
||
expect(pageData).toEqual({ | ||
totalItems: 2, | ||
dataItems: [TestDataItemFive, TestDataItemFour], | ||
}) | ||
}) | ||
}) |
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,48 @@ | ||
import { expect, test, describe } from 'vitest' | ||
import { justADatePlease, parseDate, getMinDate } from './DateUtils' | ||
|
||
describe('justADatePlease', () => { | ||
test('undefined', () => { | ||
expect(justADatePlease(undefined)).toEqual(getMinDate()) | ||
}) | ||
|
||
test('null', () => { | ||
expect(justADatePlease(null)).toEqual(getMinDate()) | ||
}) | ||
|
||
test('date', () => { | ||
const date = new Date(2021, 0, 1, 1, 2, 3, 4) | ||
expect(justADatePlease(date)).toEqual(new Date(Date.UTC(2021, 0, 1, 0, 0, 0, 0))) | ||
}) | ||
|
||
test('date with offset rollover on day', () => { | ||
const date = new Date(2021, 0, 1, 22, 2, 3, 4) | ||
expect(justADatePlease(date)).toEqual(new Date(Date.UTC(2021, 0, 2, 0, 0, 0, 0))) | ||
}) | ||
}) | ||
|
||
describe('parseDate', () => { | ||
test('undefined', () => { | ||
expect(parseDate(undefined)).toEqual(getMinDate()) | ||
}) | ||
|
||
test('null', () => { | ||
expect(parseDate(null)).toEqual(getMinDate()) | ||
}) | ||
|
||
test('empty', () => { | ||
expect(parseDate('')).toEqual(getMinDate()) | ||
}) | ||
|
||
test('date', () => { | ||
const dateString = '2021-01-01T06:02:03.004-05:00' | ||
const date = parseDate(dateString) | ||
expect(date).toEqual(new Date(Date.UTC(2021, 0, 1, 11, 2, 3, 4))) | ||
}) | ||
|
||
test('justADate', () => { | ||
const dateString = '2021-01-01T06:02:03.004-05:00' | ||
const date = parseDate(dateString, true) | ||
expect(date).toEqual(new Date(Date.UTC(2021, 0, 1, 0, 0, 0, 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
/** | ||
* Return a new JS date with a time of 00:00:00.000+00:00 on 1970-01-01. | ||
* @returns A new JS date with a time of 00:00:00.000+00:00 on 1970-01-01. | ||
*/ | ||
export const getMinDate = () => new Date(0) | ||
|
||
/** | ||
* Converts a date to a date with a time of 00:00:00.000. | ||
* The year, month, and day will be adjusted to UTC. | ||
* If the date is undefined or null, a minimum date of 1970-01-01T00:00:00.000Z will be returned. | ||
* @param date The date to convert. | ||
* @returns A new date with a time of 00:00:00.000. | ||
*/ | ||
export function justADatePlease(date: Date | undefined | null): Date { | ||
if (!date) { | ||
return getMinDate() | ||
} | ||
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0, 0)) | ||
} | ||
|
||
/** | ||
* Parses a date string into a JS date. | ||
* @param value The date string to parse. | ||
* @param justADate If true, the time will be set to 00:00:00.000 and the year, month, and day will be adjusted to UTC. | ||
* @returns A new date, undefined if the value was undefined, or null if the value was an empty string. | ||
*/ | ||
export function parseDate(value: string | undefined | null, justADate: boolean = false): Date { | ||
if (!value) { | ||
return getMinDate() | ||
} | ||
|
||
let resolvedValue = value | ||
if (!/(\d\d:\d\d)|Z$/.test(value)) { | ||
/** | ||
* If the value does not contain a time, assume it is a date and append a time of 00:00:00.000Z. | ||
*/ | ||
resolvedValue = `${value}T00:00:00.000Z` | ||
} | ||
|
||
let result = new Date(resolvedValue) as Date | undefined | null | ||
if (justADate) { | ||
result = justADatePlease(result) | ||
} | ||
return result as Date | ||
} |
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
Oops, something went wrong.