Skip to content

Commit c890770

Browse files
fix(Timestamp): updated logic for rendering datetime attribute (#8205)
* fix(Timestamp): updated logic for rendering datetime attribute * Updated spread props to prevent duplicate datetime attribute * Made updates per PR feedback * Added new utils file to helpers index * Removed timezone from dates in tests
1 parent 024c3af commit c890770

File tree

7 files changed

+56
-14
lines changed

7 files changed

+56
-14
lines changed

packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import AngleRightIcon from '@patternfly/react-icons/dist/esm/icons/angle-right-i
88
import { css } from '@patternfly/react-styles';
99
import styles from '@patternfly/react-styles/css/components/CalendarMonth/calendar-month';
1010
import { getUniqueId } from '../../helpers/util';
11+
import { isValidDate } from '../../helpers/datetimeUtils';
1112

1213
export enum Weekday {
1314
Sunday = 0,
@@ -109,8 +110,6 @@ const buildCalendar = (year: number, month: number, weekStart: number, validator
109110
const isSameDate = (d1: Date, d2: Date) =>
110111
d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate();
111112

112-
export const isValidDate = (date: Date) => Boolean(date && !isNaN(date as any));
113-
114113
const today = new Date();
115114

116115
/** The main calendar month component. */

packages/react-core/src/components/DatePicker/DatePicker.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import { TextInput, TextInputProps } from '../TextInput/TextInput';
66
import { Popover, PopoverProps } from '../Popover/Popover';
77
import { InputGroup } from '../InputGroup/InputGroup';
88
import OutlinedCalendarAltIcon from '@patternfly/react-icons/dist/esm/icons/outlined-calendar-alt-icon';
9-
import { CalendarMonth, CalendarFormat, isValidDate } from '../CalendarMonth';
9+
import { CalendarMonth, CalendarFormat } from '../CalendarMonth';
1010
import { useImperativeHandle } from 'react';
1111
import { KeyTypes } from '../../helpers';
12+
import { isValidDate } from '../../helpers/datetimeUtils';
1213

1314
/** The main date picker component. */
1415

packages/react-core/src/components/Timestamp/Timestamp.tsx

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
22
import styles from '@patternfly/react-styles/css/components/Timestamp/timestamp';
33
import { css } from '@patternfly/react-styles';
44
import { Tooltip } from '../Tooltip';
5+
import { isValidDate } from '../../helpers/datetimeUtils';
56

67
export enum TimestampFormat {
78
full = 'full',
@@ -78,7 +79,7 @@ export const Timestamp: React.FunctionComponent<TimestampProps> = ({
7879
children,
7980
className,
8081
customFormat,
81-
date: dateProp = new Date(),
82+
date: dateProp,
8283
dateFormat,
8384
displaySuffix = '',
8485
is12Hour,
@@ -87,34 +88,54 @@ export const Timestamp: React.FunctionComponent<TimestampProps> = ({
8788
tooltip,
8889
...props
8990
}: TimestampProps) => {
91+
const [date, setDate] = React.useState(() => {
92+
const initDate = new Date(dateProp);
93+
if (isValidDate(initDate)) {
94+
return initDate;
95+
}
96+
97+
return new Date();
98+
});
99+
100+
React.useEffect(() => {
101+
const dateFromProp = new Date(dateProp);
102+
if (isValidDate(dateFromProp) && dateFromProp.toString() !== new Date(date).toString()) {
103+
setDate(dateFromProp);
104+
} else if (!dateProp) {
105+
setDate(new Date());
106+
}
107+
}, [dateProp]);
108+
90109
const hasTimeFormat = timeFormat && !customFormat;
91110
const formatOptions = {
92111
...(dateFormat && !customFormat && { dateStyle: dateFormat }),
93112
...(customFormat && { ...customFormat }),
94113
...(is12Hour !== undefined && { hour12: is12Hour })
95114
};
96115

97-
const dateAsLocaleString = new Date(dateProp).toLocaleString(locale, {
116+
const dateAsLocaleString = new Date(date).toLocaleString(locale, {
98117
...formatOptions,
99118
...(hasTimeFormat && { timeStyle: timeFormat })
100119
});
101120
const defaultDisplay = `${dateAsLocaleString}${displaySuffix ? ' ' + displaySuffix : ''}`;
102121

103122
const utcTimeFormat = timeFormat !== 'short' ? 'medium' : 'short';
104123
const convertToUTCString = (date: Date) => new Date(date).toUTCString().slice(0, -3);
105-
const utcDateString = new Date(convertToUTCString(dateProp)).toLocaleString(locale, {
124+
const utcDateString = new Date(convertToUTCString(date)).toLocaleString(locale, {
106125
...formatOptions,
107126
...(hasTimeFormat && { timeStyle: utcTimeFormat })
108127
});
109128
const defaultTooltipContent = `${utcDateString}${tooltip?.suffix ? ' ' + tooltip.suffix : ' UTC'}`;
110129

130+
const { dateTime, ...propsWithoutDateTime } = props;
131+
111132
const timestamp = (
112133
<span
113134
className={css(styles.timestamp, tooltip && styles.modifiers.helpText, className)}
114135
{...(tooltip && { tabIndex: 0 })}
115-
{...props}
136+
{...propsWithoutDateTime}
116137
>
117-
<time className="pf-c-timestamp__text" dateTime={props.dateTime || new Date(dateProp).toISOString()}>
138+
<time className="pf-c-timestamp__text" dateTime={dateTime || new Date(date).toISOString()}>
118139
{!children ? defaultDisplay : children}
119140
</time>
120141
</span>

packages/react-core/src/components/Timestamp/__tests__/Timestamp.test.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,31 @@ test('Renders with current date by default with default formatting', () => {
3636
expect(screen.getByText(new Date().toLocaleString())).toBeInTheDocument();
3737
});
3838

39+
test('Renders with correct datetime attribute with current date by default', () => {
40+
render(<Timestamp />);
41+
// Because there could be a .001 ms difference in the expected and received datetime value,
42+
// we want an ISO value without the ms to expect as the datetime value.
43+
const isoDateWithoutMS = new Date().toISOString().split('.')[0];
44+
45+
expect(screen.getByText(new Date().toLocaleString())).toHaveAttribute(
46+
'datetime',
47+
expect.stringMatching(isoDateWithoutMS)
48+
);
49+
});
50+
3951
test('Renders passed in date with default formatting', () => {
4052
render(<Timestamp date={new Date(2022, 0, 1)} />);
4153

4254
expect(screen.getByText('1/1/2022, 12:00:00 AM')).toBeInTheDocument();
4355
});
4456

57+
test('Renders with correct datetime attribute when date is passed in', () => {
58+
const passedDate = new Date(2022, 0, 1);
59+
render(<Timestamp date={passedDate} />);
60+
61+
expect(screen.getByText('1/1/2022, 12:00:00 AM')).toHaveAttribute('datetime', passedDate.toISOString());
62+
});
63+
4564
test('Renders with custom formatting when dateFormat and timeFormat are passed in', () => {
4665
render(
4766
<Timestamp date={new Date(2022, 0, 1)} dateFormat={TimestampFormat.full} timeFormat={TimestampFormat.short} />
@@ -51,7 +70,7 @@ test('Renders with custom formatting when dateFormat and timeFormat are passed i
5170
});
5271

5372
test('Renders with only date when dateFormat is passed in', () => {
54-
render(<Timestamp date={new Date('1 Jan 2022 00:00:00 EST')} dateFormat={TimestampFormat.full} />);
73+
render(<Timestamp date={new Date(2022, 0, 1)} dateFormat={TimestampFormat.full} />);
5574

5675
expect(screen.getByText('Saturday, January 1, 2022')).toBeInTheDocument();
5776
});
@@ -152,17 +171,15 @@ test('Renders with pf-m-help-text class when tooltip is passed in with custom va
152171
});
153172

154173
test('Renders with default tooltip content for default variant', () => {
155-
render(
156-
<Timestamp date={new Date('1 Jan 2022 00:00:00 EST')} tooltip={{ variant: TimestampTooltipVariant.default }} />
157-
);
174+
render(<Timestamp date={new Date(2022, 0, 1, 0, 0, 0)} tooltip={{ variant: TimestampTooltipVariant.default }} />);
158175

159176
expect(screen.getByText('1/1/2022, 5:00:00 AM UTC')).toBeInTheDocument();
160177
});
161178

162179
test('Renders with custom tooltip suffix for default variant', () => {
163180
render(
164181
<Timestamp
165-
date={new Date('1 Jan 2022 00:00:00 EST')}
182+
date={new Date(2022, 0, 1, 0, 0, 0)}
166183
tooltip={{ variant: TimestampTooltipVariant.default, suffix: 'Coordinated Universal Time' }}
167184
/>
168185
);

packages/react-core/src/components/Timestamp/__tests__/__snapshots__/Timestamp.test.tsx.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ exports[`Matches snapshot 1`] = `
44
<DocumentFragment>
55
<span
66
class="pf-c-timestamp"
7-
datetime="2022-01-01T00:00:00.000Z"
87
>
98
<time
109
class="pf-c-timestamp__text"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* @param {Date} date - A date to check the validity of
3+
*/
4+
export const isValidDate = (date: Date) => Boolean(date && !isNaN(date as any));

packages/react-core/src/helpers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export * from './useIsomorphicLayout';
1010
export * from './KeyboardHandler';
1111
export * from './resizeObserver';
1212
export * from './useInterval';
13+
export * from './datetimeUtils';

0 commit comments

Comments
 (0)