From 392ed658bb416783e73451c7fee31515bbd04598 Mon Sep 17 00:00:00 2001 From: Linus Pahl Date: Fri, 20 Oct 2023 11:36:23 +0200 Subject: [PATCH] Improve documentation how to handle date times in the graylog frontend. (#17007) * Extend documentation for date time handling. * Use h2 instead of h3 for ux patterns headlines. * Move documentation into UserDateTimeProvider. * Add documentation for Timestamp component. * Extend documentation for date tiem utils and RelativeTime component. * Improve documentation for toUTCFromTz date time util. * Cleanup * Fix toUTCFromTz test. --- .../docs/StyleGuideWrapper.tsx | 11 ++++-- .../docs/common-functionality.md | 17 ++++++++- graylog2-web-interface/docs/ux-patterns.md | 4 +- .../src/components/common/RelativeTime.md | 5 +++ .../src/components/common/RelativeTime.tsx | 3 +- .../src/components/common/Timestamp.md | 38 +++++++++++++++++++ .../src/components/common/Timestamp.tsx | 2 +- .../src/contexts/UserDateTimeProvider.tsx | 5 +++ .../src/util/DateTime.test.ts | 4 +- graylog2-web-interface/src/util/DateTime.ts | 30 ++++++++++++++- graylog2-web-interface/styleguide.config.js | 1 + 11 files changed, 106 insertions(+), 14 deletions(-) create mode 100644 graylog2-web-interface/src/components/common/RelativeTime.md create mode 100644 graylog2-web-interface/src/components/common/Timestamp.md diff --git a/graylog2-web-interface/docs/StyleGuideWrapper.tsx b/graylog2-web-interface/docs/StyleGuideWrapper.tsx index 8307d29164cd..76e7ae3394f7 100644 --- a/graylog2-web-interface/docs/StyleGuideWrapper.tsx +++ b/graylog2-web-interface/docs/StyleGuideWrapper.tsx @@ -23,6 +23,7 @@ import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import GraylogThemeProvider from '../src/theme/GraylogThemeProvider'; import CurrentUserContext from '../src/contexts/CurrentUserContext'; import User from '../src/logic/users/User'; +import UserDateTimeProvider from '../src/contexts/UserDateTimeProvider'; /* eslint-enable import/no-relative-packages */ export const adminUser = User.builder() @@ -58,10 +59,12 @@ const StyleGuideWrapper = ({ children }: Props) => { path: '/:url?', element: ( - - - {children} - + + + + {children} + + ), }]); diff --git a/graylog2-web-interface/docs/common-functionality.md b/graylog2-web-interface/docs/common-functionality.md index 076cd84f0730..9c8869a7737e 100644 --- a/graylog2-web-interface/docs/common-functionality.md +++ b/graylog2-web-interface/docs/common-functionality.md @@ -1,5 +1,20 @@ -### Setting up keyboard shortcuts +## Setting up keyboard shortcuts If you want to add a keyboard shortcut, you need to: - extend the `hotkeysCollections` in the `hotkeysProvider`. - call `useHotkey` hook in the place which provides the functionality you want to execute on keypress. + +## Handling date times + +When receiving or sending dates to the backend they are always in UTC and expressed according to ISO 8601, for example `2010-07-30T16:03:25.000Z`. +This is also the preferred format to store date times in the state of, for example, UI components. + +When displaying date times in the UI they are always displayed in the user timezone. +The [UserDateTimeProvider](https://github.com/Graylog2/graylog2-server/blob/master/graylog2-web-interface/src/contexts/UserDateTimeProvider.tsx) contains the related functionality +and provides further information. It can be access using the [useUserDateTime](https://github.com/Graylog2/graylog2-server/blob/master/graylog2-web-interface/src/hooks/useUserDateTime.ts) hook. + +If you just want to display a date time, you can render the [Timestamp](#timestamp) component, which implements methods provided by the `useUserDateTime` hook. +If you want to display the relative time in a human-readable format you can render the [RelativeTime](#relativetime) component. + +For all other cases where you need to transform a date time you can use the [DateTime](https://github.com/Graylog2/graylog2-server/blob/master/graylog2-web-interface/src/util/DateTime.ts) utils. +Instead of using `moment` directly, use or (if necessary) extend the `DateTime` utils. It makes it easier to replace moment with an alternative. diff --git a/graylog2-web-interface/docs/ux-patterns.md b/graylog2-web-interface/docs/ux-patterns.md index 9d691373acab..1081129a3782 100644 --- a/graylog2-web-interface/docs/ux-patterns.md +++ b/graylog2-web-interface/docs/ux-patterns.md @@ -1,4 +1,4 @@ -### Form & Modal Submit Buttons +## Form & Modal Submit Buttons - Rely on the shared components `FormSubmit` and `ModalSubmit` to implement the submit and cancel button. The `FormSubmit` can be used for all forms on pages. The `ModalSubmit` can be used for modals and similar element like @@ -11,7 +11,7 @@ - Make sure to write only the first letter uppercase and all other letter lowercase. - Always use `Cancel` for the cancel button name. -### `EmptyEntity` & `NoEntitiesExist` & `NoSearchResults` components +## `EmptyEntity` & `NoEntitiesExist` & `NoSearchResults` components - These three components are closely related and maybe confusing to decide which one to use for which situation. - `EmptyEntity` should be used to display a message for an entity that does not have any entries in the database yet. This components supports displaying a message explaining what the entity is and can also support including a button link to create one via the children props diff --git a/graylog2-web-interface/src/components/common/RelativeTime.md b/graylog2-web-interface/src/components/common/RelativeTime.md new file mode 100644 index 000000000000..e9495a5c86ae --- /dev/null +++ b/graylog2-web-interface/src/components/common/RelativeTime.md @@ -0,0 +1,5 @@ +Relative time since `2010-07-30T16:03:25.000Z`. + +```tsx + +``` diff --git a/graylog2-web-interface/src/components/common/RelativeTime.tsx b/graylog2-web-interface/src/components/common/RelativeTime.tsx index 840a9551254a..924f3a1c4285 100644 --- a/graylog2-web-interface/src/components/common/RelativeTime.tsx +++ b/graylog2-web-interface/src/components/common/RelativeTime.tsx @@ -25,9 +25,8 @@ type Props = { }; /** - * This component receives any date time and displays the relative time until now in a human readable format. + * This component receives any date time and displays the relative time until now in a human-readable format. */ - const RelativeTime = ({ dateTime: dateTimeProp }: Props) => { const dateTime = dateTimeProp ?? new Date(); const relativeTime = relativeDifference(dateTime); diff --git a/graylog2-web-interface/src/components/common/Timestamp.md b/graylog2-web-interface/src/components/common/Timestamp.md new file mode 100644 index 000000000000..423088d5b11f --- /dev/null +++ b/graylog2-web-interface/src/components/common/Timestamp.md @@ -0,0 +1,38 @@ +For the following examples we are using the date time `2010-07-30T16:03:25.000Z`. +By default, the output will be based on the user time zone defined for the style guide. + +#### Default + +The component displays the date time in the default format for date times in the graylog UI, when no format is specified. + +```tsx + +``` + +#### Specific timezone + +In this example we are displaying the provided time as UTC. + +```tsx + +``` + +#### Different formats + +```tsx +import { DATE_TIME_FORMATS } from 'util/DateTime'; + + + + + + + + {Object.keys(DATE_TIME_FORMATS).map((format) => ( + + + + + ))} +
FormatOutput
{format}
+``` diff --git a/graylog2-web-interface/src/components/common/Timestamp.tsx b/graylog2-web-interface/src/components/common/Timestamp.tsx index 6e2a3af8d5ca..c1f0c4ed25c8 100644 --- a/graylog2-web-interface/src/components/common/Timestamp.tsx +++ b/graylog2-web-interface/src/components/common/Timestamp.tsx @@ -34,7 +34,7 @@ type Props = { /** * Component that renders a given date time based on the user time zone in a `time` HTML element. * It is capable of render date times in different formats, accepting ISO 8601 - * strings, JS native Date objects, and Moment.js Date objects. + * strings, JS native Date objects, and Moment.js Date objects. On hover the component displays the time in UTC. * * While the component is using the user time zone by default, it is also possible * to change the time zone for the given date, something that helps, for instance, to display a local time diff --git a/graylog2-web-interface/src/contexts/UserDateTimeProvider.tsx b/graylog2-web-interface/src/contexts/UserDateTimeProvider.tsx index b10e80b7a0ba..61eb9b3e2848 100644 --- a/graylog2-web-interface/src/contexts/UserDateTimeProvider.tsx +++ b/graylog2-web-interface/src/contexts/UserDateTimeProvider.tsx @@ -39,6 +39,11 @@ const getUserTimezone = (userTimezone: string, tzOverride?: string) => { /** * Provides methods to convert times based on the user time zone. * Should be used when displaying times and the related components are not a suitable option. + * + * userTimezone - time zone of the current user. + * formatTime - method which takes a date and optionally a format and returns it as a string in the current user timezone. + * For example, it transforms `2010-07-30T16:03:25.000Z` to `2010-07-30 17:03:25` for a user with the timezone `Europe/Berlin`. + * toUserTimezone - method which takes a date and transforms it a moment date object, based on the user timezone. */ const StaticTimezoneProvider = ({ children, tz }: Required) => { diff --git a/graylog2-web-interface/src/util/DateTime.test.ts b/graylog2-web-interface/src/util/DateTime.test.ts index 347fc594f9ca..efcbdf8b53d0 100644 --- a/graylog2-web-interface/src/util/DateTime.test.ts +++ b/graylog2-web-interface/src/util/DateTime.test.ts @@ -172,11 +172,11 @@ describe('DateTime utils', () => { describe('toUTCFromTz', () => { it('should transform time to UTC based on defined tz', () => { - expect(adjustFormat(toUTCFromTz('2020-01-01T10:00:00.000', moscowTZ), 'internal')).toBe('2020-01-01T07:00:00.000+00:00'); + expect(toUTCFromTz('2020-01-01T10:00:00.000', moscowTZ).toISOString()).toEqual('2020-01-01T07:00:00.000Z'); }); it('should prioritize time zone of date time over provided time zone when calculating UTC time', () => { - expect(adjustFormat(toUTCFromTz('2020-01-01T12:00:00.000+02:00', 'Europe/Berlin'), 'internal')).toBe('2020-01-01T10:00:00.000+00:00'); + expect(toUTCFromTz('2020-01-01T12:00:00.000+05:00', 'Europe/Berlin').toISOString()).toBe('2020-01-01T07:00:00.000Z'); }); }); }); diff --git a/graylog2-web-interface/src/util/DateTime.ts b/graylog2-web-interface/src/util/DateTime.ts index 9c7a9c33cf74..1e3432cbd4d0 100644 --- a/graylog2-web-interface/src/util/DateTime.ts +++ b/graylog2-web-interface/src/util/DateTime.ts @@ -62,6 +62,10 @@ const getFormatStringsForDateTimeFormats = (dateTimeFormats: Array, tz = DEFAULT_OUTPUT_TZ) => { const acceptedFormatStrings = getFormatStringsForDateTimeFormats(acceptedFormats); const dateObject = moment(dateTime, acceptedFormatStrings, true).tz(tz); @@ -70,24 +74,46 @@ export const toDateObject = (dateTime: DateTime, acceptedFormats?: Array toDateObject(dateTimeString, ['internal'], tz); +/** + * Returns the estimated browser time zone. + */ export const getBrowserTimezone = () => moment.tz.guess(); +/** + * Returns the provided date time as a string, based on the targeted format and timezone. + */ export const adjustFormat = (dateTime: DateTime, format: DateTimeFormats = 'default', tz = DEFAULT_OUTPUT_TZ) => toDateObject(dateTime, undefined, tz).format(DATE_TIME_FORMATS[format]); +/** + * Returns the provided date time as a string, based on the targeted format and the browser timezone. + */ export const formatAsBrowserTime = (time: DateTime, format: DateTimeFormats = 'default') => adjustFormat(time, format, getBrowserTimezone()); +/** + * Returns the time in a human-readable format, relative to the provided date time. + * If you just want to display the output, you can use the `RelativeTime` component. + */ export const relativeDifference = (dateTime: DateTime) => { const dateObject = toDateObject(dateTime); return validateDateTime(dateObject, dateTime).fromNow(); }; +/** + * Validate if the provided time has a supported format. + */ export const isValidDate = (dateTime: DateTime) => moment(dateTime, Object.values(DATE_TIME_FORMATS), true).isValid(); -// This function allows transforming a date time, which does not contain a time zone like `2010-01-01 10:00:00`, to UTC. -// For this calculation it is necessary to define the time zone of the provided date time. +/** + * This function transforms the provided date time to UTC, based on the defined time zone. + * This is useful for date times like `2010-01-01 10:00:00`, which do not include the timezone information. + * For this calculation it is necessary to define the timezone which the date time is currently based on. + */ export const toUTCFromTz = (dateTime: string, sourceTimezone: string) => { if (!sourceTimezone) { throw new Error('It is required to define the time zone of the date time provided for internalUTCTime.'); diff --git a/graylog2-web-interface/styleguide.config.js b/graylog2-web-interface/styleguide.config.js index 09a42ee026d4..5a14eb54c9e0 100644 --- a/graylog2-web-interface/styleguide.config.js +++ b/graylog2-web-interface/styleguide.config.js @@ -29,6 +29,7 @@ const defaultComponentIgnore = [ ]; module.exports = { + skipComponentsWithoutExample: true, require: [ 'core-js/stable', 'regenerator-runtime/runtime',