Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ae6cdbb
[EuiTimeZoneDisplay] New component
acstll Nov 6, 2025
0237cd1
[EuiSuperDatePicker] Integrate time zone display
acstll Nov 6, 2025
acbd81d
[EuiTimeZoneDisplay] locale not actually needed
acstll Nov 6, 2025
9bf0081
[EuiTimeZoneDisplay] Add story
acstll Nov 6, 2025
34a77ea
[TimeWindowButtons] Try fixing flaky test
acstll Nov 6, 2025
f51c969
Lint, remove smoke testing leftover
acstll Nov 6, 2025
91c3751
[EuiTimeZoneDisplay] Rename hook and add eui prefix
acstll Nov 7, 2025
7748688
TODO comment
acstll Nov 7, 2025
216f083
[EuiTimeZoneDisplay] Add unit tests
acstll Nov 7, 2025
f0a2a79
Update VRT
acstll Nov 7, 2025
954232f
[Docs] Time zone display
acstll Nov 7, 2025
58d8029
Lint
acstll Nov 7, 2025
1028a20
Changelog
acstll Nov 7, 2025
651d787
[TimeWindowButtons] Try fixing flaky test 2
acstll Nov 7, 2025
dcce4f0
[TimeWindowButtons] Try fixing flaky test 3
acstll Nov 7, 2025
cf487a8
[EuiTimeZoneDisplay] Refactor props, use timeZoneDisplayProps
acstll Nov 11, 2025
9a20e95
[Docs] Add missing aria-label to EuiButtonIcon
acstll Nov 12, 2025
a990bf5
[EuiTimeZoneDisplay] Link inputs via aria-describedby
acstll Nov 12, 2025
bb9904b
[EuiSuperDatePicker] Show time zone by default in stories
acstll Nov 12, 2025
3d010a3
[Docs] Update time zone bit
acstll Nov 12, 2025
4219bbc
Polish comments, JSDoc
acstll Nov 12, 2025
325881e
Update snapshots
acstll Nov 12, 2025
5e32411
[Docs] Fix snippet
acstll Nov 12, 2025
b1737aa
Update changelog
acstll Nov 12, 2025
64938bb
[Nit] Keep interface as it was
acstll Nov 12, 2025
dcda47f
Typo
acstll Nov 12, 2025
7387079
Fix Storybook interaction
acstll Nov 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions packages/eui/changelogs/upcoming/9191.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Updated `EuiSuperDatePicker` with new time zone information, opt-in via `timeZoneDisplay`.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiTimeZoneDisplay renders 1`] = `
<div>
<div
class="euiFlexGroup emotion-euiFlexGroup-responsive-xs-flexStart-center-row-euiTimeZoneDisplay"
data-test-subj="euiTimeZoneDisplay"
>
<span
color="subdued"
data-euiicon-type="globe"
/>
<span
class="euiText emotion-euiText-s-euiTextColor-subdued"
>
UTC-8

(America/Los_Angeles)
</span>
</div>
</div>
`;
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ describe('EuiAbsoluteTab', () => {

describe('allows several other common date formats, and autoformats them to the `dateFormat` prop', () => {
const assertOutput = (input: HTMLInputElement) => {
// Exclude hours from assertion, because moment uses local machine timezone
// Exclude hours from assertion, because moment uses local machine time zone
expect(input.value).toContain('Jan 1, 1970');
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import { EuiCode } from '../../../code';

import { EuiDatePicker, EuiDatePickerProps } from '../../date_picker';
import { EuiDatePopoverContentProps } from './date_popover_content';
import {
EuiTimeZoneDisplay,
type EuiTimeZoneDisplayProps,
} from './timezone_display';
import { euiAbsoluteTabDateFormStyles } from './absolute_tab.styles';

// Allow users to paste in and have the datepicker parse multiple common date formats,
Expand All @@ -36,7 +40,7 @@ const ALLOWED_USER_DATE_FORMATS = [
'X', // Unix timestamp in seconds
];

export interface EuiAbsoluteTabProps {
export type EuiAbsoluteTabProps = {
dateFormat: string;
timeFormat: string;
locale?: LocaleSpecifier;
Expand All @@ -47,7 +51,7 @@ export interface EuiAbsoluteTabProps {
utcOffset?: number;
minDate?: Moment;
maxDate?: Moment;
}
} & EuiTimeZoneDisplayProps;

export const EuiAbsoluteTab: FunctionComponent<EuiAbsoluteTabProps> = ({
value,
Expand All @@ -60,6 +64,8 @@ export const EuiAbsoluteTab: FunctionComponent<EuiAbsoluteTabProps> = ({
minDate,
maxDate,
labelPrefix,
timeZoneDisplay,
timeZoneCustomDisplayRender,
}) => {
const styles = useEuiMemoizedStyles(euiAbsoluteTabDateFormStyles);

Expand Down Expand Up @@ -212,6 +218,11 @@ export const EuiAbsoluteTab: FunctionComponent<EuiAbsoluteTabProps> = ({
/>
)}
</EuiFlexGroup>
<EuiTimeZoneDisplay
timeZoneDisplay={timeZoneDisplay}
timeZoneCustomDisplayRender={timeZoneCustomDisplayRender}
date={minDate}
/>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ import {
EuiDatePopoverContent,
EuiDatePopoverContentProps,
} from './date_popover_content';
import { type EuiTimeZoneDisplayProps } from './timezone_display';

import { euiDatePopoverButtonStyles } from './date_popover_button.styles';

export interface EuiDatePopoverButtonProps {
export type EuiDatePopoverButtonProps = {
className?: string;
buttonProps?: CommonProps & ButtonHTMLAttributes<HTMLButtonElement>;
dateFormat: string;
Expand All @@ -50,7 +51,7 @@ export interface EuiDatePopoverButtonProps {
maxDate?: Moment;
compressed?: boolean;
timeOptions: TimeOptions;
}
} & EuiTimeZoneDisplayProps;

export const EuiDatePopoverButton: FunctionComponent<
EuiDatePopoverButtonProps
Expand All @@ -76,6 +77,8 @@ export const EuiDatePopoverButton: FunctionComponent<
onPopoverClose,
compressed,
timeOptions,
timeZoneDisplay,
timeZoneCustomDisplayRender,
...rest
} = props;

Expand Down Expand Up @@ -157,6 +160,8 @@ export const EuiDatePopoverButton: FunctionComponent<
timeOptions={timeOptions}
minDate={minDate}
maxDate={maxDate}
timeZoneDisplay={timeZoneDisplay}
timeZoneCustomDisplayRender={timeZoneCustomDisplayRender}
/>
</EuiPopover>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ import {
import { EuiAbsoluteTab } from './absolute_tab';
import { EuiRelativeTab } from './relative_tab';
import { euiDatePopoverContentStyles } from './date_popover_content.styles';
import { type EuiTimeZoneDisplayProps } from './timezone_display';

export interface EuiDatePopoverContentProps {
export type EuiDatePopoverContentProps = {
value: string;
onChange: (date: string) => void;
canRoundRelativeUnits?: boolean;
Expand All @@ -40,7 +41,7 @@ export interface EuiDatePopoverContentProps {
minDate?: Moment;
maxDate?: Moment;
timeOptions: TimeOptions;
}
} & EuiTimeZoneDisplayProps;

export const EuiDatePopoverContent: FunctionComponent<
EuiDatePopoverContentProps
Expand All @@ -57,6 +58,8 @@ export const EuiDatePopoverContent: FunctionComponent<
timeOptions,
minDate,
maxDate,
timeZoneDisplay,
timeZoneCustomDisplayRender,
}) => {
const styles = useEuiMemoizedStyles(euiDatePopoverContentStyles);

Expand Down Expand Up @@ -107,6 +110,8 @@ export const EuiDatePopoverContent: FunctionComponent<
utcOffset={utcOffset}
minDate={minDate}
maxDate={maxDate}
timeZoneDisplay={timeZoneDisplay}
timeZoneCustomDisplayRender={timeZoneCustomDisplayRender}
/>
),
'data-test-subj': 'superDatePickerAbsoluteTab',
Expand All @@ -126,6 +131,8 @@ export const EuiDatePopoverContent: FunctionComponent<
roundUp={roundUp}
labelPrefix={labelPrefix}
timeOptions={timeOptions}
timeZoneDisplay={timeZoneDisplay}
timeZoneCustomDisplayRender={timeZoneCustomDisplayRender}
/>
),
'data-test-subj': 'superDatePickerRelativeTab',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,20 @@ import {
toRelativeStringFromParts,
} from '../relative_utils';
import { EuiDatePopoverContentProps } from './date_popover_content';
import {
EuiTimeZoneDisplay,
type EuiTimeZoneDisplayProps,
} from './timezone_display';

export interface EuiRelativeTabProps {
export type EuiRelativeTabProps = {
dateFormat: string;
locale?: LocaleSpecifier;
value: string;
onChange: EuiDatePopoverContentProps['onChange'];
roundUp?: boolean;
labelPrefix: string;
timeOptions: TimeOptions;
}
} & EuiTimeZoneDisplayProps;

export const EuiRelativeTab: FunctionComponent<EuiRelativeTabProps> = ({
timeOptions: { relativeOptions, relativeRoundingLabels },
Expand All @@ -62,6 +66,8 @@ export const EuiRelativeTab: FunctionComponent<EuiRelativeTabProps> = ({
onChange,
roundUp,
labelPrefix,
timeZoneDisplay,
timeZoneCustomDisplayRender,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Unrelated to the marked line]:

I noticed that the prepend on the EuiFieldText is not linked to the input (code), meaning the input has no label (the same applies in other tabs).

We don't have to fix it here if you prefer keeping it scoped, but maybe you could add it at some point where it fits in while working on EuiSuperDatePicker?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice catch, if I don't do it here, I'll make sure to do it while working on EuiSuperDatePicker as you suggest

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do this in a separate PR 🫡

}) => {
const initialRelativeParts = useRef<RelativeParts>(parseRelativeParts(value));
const { roundUnit } = initialRelativeParts.current;
Expand Down Expand Up @@ -186,6 +192,10 @@ export const EuiRelativeTab: FunctionComponent<EuiRelativeTabProps> = ({
</p>
</EuiScreenReaderOnly>
</EuiForm>
<EuiTimeZoneDisplay
Comment thread
mgadewoll marked this conversation as resolved.
timeZoneDisplay={timeZoneDisplay}
timeZoneCustomDisplayRender={timeZoneCustomDisplayRender}
/>
<EuiPopoverFooter paddingSize="s">
<EuiSwitch
data-test-subj="superDatePickerRelativeDateRoundSwitch"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { css } from '@emotion/react';

import { UseEuiTheme } from '../../../../services';
import { logicalCSS } from '../../../../global_styling';

export const euiTimeZoneDisplayStyles = (euiThemeContext: UseEuiTheme) => {
const { euiTheme } = euiThemeContext;

// This padding should probably not be part of this component to make it really reusable

return {
euiTimeZoneDisplay: css`
${logicalCSS('padding-horizontal', euiTheme.size.s)}
${logicalCSS('padding-bottom', euiTheme.size.s)}
`,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';

import { render } from '../../../../test/rtl';

import { EuiTimeZoneDisplay } from './timezone_display';

import { EuiButtonIcon } from '../../../button';

describe('EuiTimeZoneDisplay', () => {
it('renders', () => {
const { container } = render(
<EuiTimeZoneDisplay timeZoneDisplay="America/Los_Angeles" />
);

expect(container).toMatchSnapshot();
});

it('handles "Browser" time zone name', () => {
const { getByTestSubject } = render(
<EuiTimeZoneDisplay timeZoneDisplay="Browser" />
);

const browserTimeZone = new Intl.DateTimeFormat().resolvedOptions()
.timeZone;

expect(getByTestSubject('euiTimeZoneDisplay')).toHaveTextContent(
browserTimeZone
);
});

it('handles "UTC" time zone name', () => {
const { getByTestSubject } = render(
<EuiTimeZoneDisplay timeZoneDisplay="UTC" />
);

// No name displayed between parenthesis, only "UTC"
expect(getByTestSubject('euiTimeZoneDisplay')).toHaveTextContent('UTC');
});

it('does not render with invalid time zone', () => {
const { queryByTestSubject } = render(
<EuiTimeZoneDisplay timeZoneDisplay="Foo/Bar" />
);

expect(queryByTestSubject('euiTimeZoneDisplay')).not.toBeInTheDocument();
});

test('timeZoneCustomDisplayRender render function', () => {
const customContent = (
<EuiButtonIcon
href="https://example.com"
iconType="documentation"
data-test-subj="customContent"
/>
);

const { getByTestSubject } = render(
<EuiTimeZoneDisplay
timeZoneDisplay="Europe/Helsinki"
timeZoneCustomDisplayRender={({ nameDisplay }) => (
<>
{nameDisplay}
{customContent}
</>
)}
/>
);

expect(getByTestSubject('euiTimeZoneDisplay')).toHaveTextContent(
'Helsinki'
);
expect(getByTestSubject('customContent')).toBeInTheDocument();
});
});
Loading