Skip to content

Commit 284121c

Browse files
authored
fix: formatting blank date with Tempo shouldn't throw (#2224)
1 parent 2948169 commit 284121c

File tree

3 files changed

+118
-30
lines changed

3 files changed

+118
-30
lines changed

packages/common/src/formatters/__tests__/formatterUtilities.spec.ts

Lines changed: 101 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,41 +30,79 @@ describe('formatterUtilities', () => {
3030

3131
beforeEach(() => {
3232
columnDefinitions = [
33-
{ id: 'firstName', field: 'firstName', editor: { model: Editors.text } },
33+
{
34+
id: 'firstName',
35+
field: 'firstName',
36+
editor: { model: Editors.text },
37+
},
3438
{
3539
id: 'lastName',
3640
field: 'lastName',
3741
editor: { model: Editors.text },
3842
formatter: multipleFormatter,
3943
params: { formatters: [myItalicFormatter, myBoldFormatter] },
4044
},
41-
{ id: 'age', field: 'age', type: 'number', formatter: multipleFormatter },
42-
{ id: 'address', field: 'address.street', editor: { model: Editors.longText }, formatter: complexObjectFormatter },
43-
{ id: 'zip', field: 'address.zip', type: 'number', formatter: complexObjectFormatter },
45+
{
46+
id: 'age',
47+
field: 'age',
48+
type: 'number',
49+
formatter: multipleFormatter,
50+
},
51+
{
52+
id: 'address',
53+
field: 'address.street',
54+
editor: { model: Editors.longText },
55+
formatter: complexObjectFormatter,
56+
},
57+
{
58+
id: 'zip',
59+
field: 'address.zip',
60+
type: 'number',
61+
formatter: complexObjectFormatter,
62+
},
4463
];
4564
});
4665

4766
it('should have custom editor formatter with correct structure', () => {
4867
autoAddEditorFormatterToColumnsWithEditor(columnDefinitions, customEditableInputFormatter);
4968

5069
expect(columnDefinitions).toEqual([
51-
{ id: 'firstName', field: 'firstName', editor: { model: Editors.text }, formatter: customEditableInputFormatter },
70+
{
71+
id: 'firstName',
72+
field: 'firstName',
73+
editor: { model: Editors.text },
74+
formatter: customEditableInputFormatter,
75+
},
5276
{
5377
id: 'lastName',
5478
field: 'lastName',
5579
editor: { model: Editors.text },
5680
formatter: multipleFormatter,
57-
params: { formatters: [myItalicFormatter, myBoldFormatter, customEditableInputFormatter] },
81+
params: {
82+
formatters: [myItalicFormatter, myBoldFormatter, customEditableInputFormatter],
83+
},
84+
},
85+
{
86+
id: 'age',
87+
field: 'age',
88+
type: 'number',
89+
formatter: multipleFormatter,
5890
},
59-
{ id: 'age', field: 'age', type: 'number', formatter: multipleFormatter },
6091
{
6192
id: 'address',
6293
field: 'address.street',
6394
editor: { model: Editors.longText },
6495
formatter: multipleFormatter,
65-
params: { formatters: [complexObjectFormatter, customEditableInputFormatter] },
96+
params: {
97+
formatters: [complexObjectFormatter, customEditableInputFormatter],
98+
},
99+
},
100+
{
101+
id: 'zip',
102+
field: 'address.zip',
103+
type: 'number',
104+
formatter: complexObjectFormatter,
66105
},
67-
{ id: 'zip', field: 'address.zip', type: 'number', formatter: complexObjectFormatter },
68106
]);
69107
});
70108

@@ -73,23 +111,42 @@ describe('formatterUtilities', () => {
73111
autoAddEditorFormatterToColumnsWithEditor(columnDefinitions, customEditableInputFormatter);
74112

75113
expect(columnDefinitions).toEqual([
76-
{ id: 'firstName', field: 'firstName', editor: { model: Editors.text }, formatter: customEditableInputFormatter },
114+
{
115+
id: 'firstName',
116+
field: 'firstName',
117+
editor: { model: Editors.text },
118+
formatter: customEditableInputFormatter,
119+
},
77120
{
78121
id: 'lastName',
79122
field: 'lastName',
80123
editor: { model: Editors.text },
81124
formatter: multipleFormatter,
82-
params: { formatters: [myItalicFormatter, myBoldFormatter, customEditableInputFormatter] },
125+
params: {
126+
formatters: [myItalicFormatter, myBoldFormatter, customEditableInputFormatter],
127+
},
128+
},
129+
{
130+
id: 'age',
131+
field: 'age',
132+
type: 'number',
133+
formatter: multipleFormatter,
83134
},
84-
{ id: 'age', field: 'age', type: 'number', formatter: multipleFormatter },
85135
{
86136
id: 'address',
87137
field: 'address.street',
88138
editor: { model: Editors.longText },
89139
formatter: multipleFormatter,
90-
params: { formatters: [complexObjectFormatter, customEditableInputFormatter] },
140+
params: {
141+
formatters: [complexObjectFormatter, customEditableInputFormatter],
142+
},
143+
},
144+
{
145+
id: 'zip',
146+
field: 'address.zip',
147+
type: 'number',
148+
formatter: complexObjectFormatter,
91149
},
92-
{ id: 'zip', field: 'address.zip', type: 'number', formatter: complexObjectFormatter },
93150
]);
94151
});
95152
});
@@ -135,9 +192,21 @@ describe('formatterUtilities', () => {
135192
expect(output).toBe('2002-01-01');
136193
});
137194

195+
it('should return same unformatted blank Date when calling the Formatter function with a blank date', () => {
196+
const formatterFn = getAssociatedDateFormatter(FieldType.dateIso, '-');
197+
const gridSpy = vi.spyOn(gridStub, 'getOptions');
198+
199+
const output = formatterFn(1, 1, '0001-01-01T00:00:00', { type: FieldType.dateIso } as Column, {}, gridStub);
200+
201+
expect(gridSpy).toHaveBeenCalled();
202+
expect(output).toBe('0001-01-01T00:00:00');
203+
});
204+
138205
it('should return a formatted Date with a different separator when changing setting the "dateSeparator" in "formatterOptions"', () => {
139206
const formatterFn = getAssociatedDateFormatter(FieldType.dateIso, '-');
140-
const gridOptions = { formatterOptions: { dateSeparator: '.' } } as GridOption;
207+
const gridOptions = {
208+
formatterOptions: { dateSeparator: '.' },
209+
} as GridOption;
141210
const gridSpy = (gridStub.getOptions as Mock).mockReturnValue(gridOptions);
142211

143212
const output = formatterFn(1, 1, '2002-01-01T00:01:01', { type: FieldType.dateIso } as Column, {}, gridStub);
@@ -164,7 +233,10 @@ describe('formatterUtilities', () => {
164233

165234
it('should return a formatted Date when calling the Formatter function with a defined "params.outputFormat"', () => {
166235
const formatterFn = getBaseDateFormatter();
167-
const mockColumn = { type: FieldType.date, params: { dateFormat: 'MMM DD, YYYY' } } as Column;
236+
const mockColumn = {
237+
type: FieldType.date,
238+
params: { dateFormat: 'MMM DD, YYYY' },
239+
} as Column;
168240

169241
const output = formatterFn(1, 1, '2002-01-01T00:01:01', mockColumn, {}, gridStub);
170242

@@ -185,8 +257,19 @@ describe('formatterUtilities', () => {
185257
};
186258

187259
beforeEach(() => {
188-
mockItem = { firstName: 'John', lastName: 'Doe', age: 45, address: { zip: 12345 }, empty: {} };
189-
mockColumn = { id: 'firstName', name: 'First Name', field: 'firstName', formatter: myUppercaseFormatter };
260+
mockItem = {
261+
firstName: 'John',
262+
lastName: 'Doe',
263+
age: 45,
264+
address: { zip: 12345 },
265+
empty: {},
266+
};
267+
mockColumn = {
268+
id: 'firstName',
269+
name: 'First Name',
270+
field: 'firstName',
271+
formatter: myUppercaseFormatter,
272+
};
190273
});
191274

192275
describe('exportWithFormatterWhenDefined method', () => {

packages/common/src/formatters/formatterUtilities.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,11 +333,11 @@ export function parseFormatterWhenExist<T = any>(
333333
/** private function to parse a date */
334334
function parseDateByIOFormats(columnDef: Column, value: any, inputDateFormat: string, outputDateFormat: string): string {
335335
const isParsingAsUtc = columnDef.params?.parseDateAsUtc ?? false;
336-
const date = tryParseDate(value, inputDateFormat);
336+
const d1 = tryParseDate(value, inputDateFormat);
337337
let outputDate = value;
338-
if (date) {
339-
const d = isParsingAsUtc ? toUtcDate(date) : date;
340-
outputDate = format(d, outputDateFormat, 'en-US');
338+
if (d1) {
339+
const d2 = isParsingAsUtc ? toUtcDate(d1) : d1;
340+
outputDate = format(d2, outputDateFormat, 'en-US');
341341
}
342342
return outputDate;
343343
}

packages/common/src/services/dateUtils.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,19 @@ export function tryParseDate(inputDate?: string | Date, inputFormat?: string, st
137137
if (!inputDate) {
138138
return false;
139139
}
140-
return inputDate instanceof Date
141-
? inputDate
142-
: parse({
143-
date: inputDate,
144-
format: inputFormat as string,
145-
dateOverflow: strict ? 'throw' : 'backward',
146-
locale: 'en-US',
147-
});
140+
const d =
141+
inputDate instanceof Date
142+
? inputDate
143+
: parse({
144+
date: inputDate,
145+
format: inputFormat as string,
146+
dateOverflow: strict ? 'throw' : 'backward',
147+
locale: 'en-US',
148+
});
149+
150+
// make sure we have a valid year before returning, otherwise return false
151+
// e.g blank date "0001-01-01" will throw with Tempo `format()`, so better return false
152+
return d.getFullYear() > 1000 ? d : false;
148153
} catch (_e) {
149154
return false;
150155
}

0 commit comments

Comments
 (0)