Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 `timeZoneDisplayProps`.

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

exports[`EuiTimeZoneDisplay renders 1`] = `
<div>
<div
aria-label="UTC-8 (America/Los_Angeles)"
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 @@ -17,7 +17,11 @@ import React, {
import moment, { Moment, LocaleSpecifier } from 'moment';
import dateMath from '@elastic/datemath';

import { useUpdateEffect, useEuiMemoizedStyles } from '../../../../services';
import {
useUpdateEffect,
useEuiMemoizedStyles,
useGeneratedHtmlId,
} from '../../../../services';
import { useEuiI18n } from '../../../i18n';
import { EuiFormRow, EuiFieldText, EuiFormLabel } from '../../../form';
import { EuiFlexGroup } from '../../../flex';
Expand All @@ -26,6 +30,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 @@ -47,6 +55,7 @@ export interface EuiAbsoluteTabProps {
utcOffset?: number;
minDate?: Moment;
maxDate?: Moment;
timeZoneDisplayProps?: EuiTimeZoneDisplayProps;
}

export const EuiAbsoluteTab: FunctionComponent<EuiAbsoluteTabProps> = ({
Expand All @@ -60,6 +69,7 @@ export const EuiAbsoluteTab: FunctionComponent<EuiAbsoluteTabProps> = ({
minDate,
maxDate,
labelPrefix,
timeZoneDisplayProps = {},
}) => {
const styles = useEuiMemoizedStyles(euiAbsoluteTabDateFormStyles);

Expand All @@ -80,6 +90,7 @@ export const EuiAbsoluteTab: FunctionComponent<EuiAbsoluteTabProps> = ({
[dateFormat]
);

const timeZomeDescriptionId = useGeneratedHtmlId();
const submitButtonLabel = useEuiI18n(
'euiAbsoluteTab.dateFormatButtonLabel',
'Parse date'
Expand Down Expand Up @@ -181,6 +192,7 @@ export const EuiAbsoluteTab: FunctionComponent<EuiAbsoluteTabProps> = ({
helpText={
hasUnparsedText && !isTextInvalid ? dateFormatError : undefined
}
describedByIds={[timeZomeDescriptionId]}
>
<EuiFieldText
compressed
Expand Down Expand Up @@ -212,6 +224,10 @@ export const EuiAbsoluteTab: FunctionComponent<EuiAbsoluteTabProps> = ({
/>
)}
</EuiFlexGroup>
<EuiTimeZoneDisplay
id={timeZomeDescriptionId}
{...timeZoneDisplayProps}
/>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
EuiDatePopoverContent,
EuiDatePopoverContentProps,
} from './date_popover_content';
import { type EuiTimeZoneDisplayProps } from './timezone_display';

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

Expand All @@ -50,6 +51,7 @@ export interface EuiDatePopoverButtonProps {
maxDate?: Moment;
compressed?: boolean;
timeOptions: TimeOptions;
timeZoneDisplayProps?: EuiTimeZoneDisplayProps;
}

export const EuiDatePopoverButton: FunctionComponent<
Expand All @@ -76,6 +78,7 @@ export const EuiDatePopoverButton: FunctionComponent<
onPopoverClose,
compressed,
timeOptions,
timeZoneDisplayProps = {},
...rest
} = props;

Expand Down Expand Up @@ -157,6 +160,7 @@ export const EuiDatePopoverButton: FunctionComponent<
timeOptions={timeOptions}
minDate={minDate}
maxDate={maxDate}
timeZoneDisplayProps={timeZoneDisplayProps}
/>
</EuiPopover>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 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 {
value: string;
Expand All @@ -40,6 +41,7 @@ export interface EuiDatePopoverContentProps {
minDate?: Moment;
maxDate?: Moment;
timeOptions: TimeOptions;
timeZoneDisplayProps?: EuiTimeZoneDisplayProps;
}

export const EuiDatePopoverContent: FunctionComponent<
Expand All @@ -57,6 +59,7 @@ export const EuiDatePopoverContent: FunctionComponent<
timeOptions,
minDate,
maxDate,
timeZoneDisplayProps = {},
}) => {
const styles = useEuiMemoizedStyles(euiDatePopoverContentStyles);

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

export interface EuiRelativeTabProps {
dateFormat: string;
Expand All @@ -52,6 +56,7 @@ export interface EuiRelativeTabProps {
roundUp?: boolean;
labelPrefix: string;
timeOptions: TimeOptions;
timeZoneDisplayProps?: EuiTimeZoneDisplayProps;
}

export const EuiRelativeTab: FunctionComponent<EuiRelativeTabProps> = ({
Expand All @@ -62,6 +67,7 @@ export const EuiRelativeTab: FunctionComponent<EuiRelativeTabProps> = ({
onChange,
roundUp,
labelPrefix,
timeZoneDisplayProps = {},
}) => {
const initialRelativeParts = useRef<RelativeParts>(parseRelativeParts(value));
const { roundUnit } = initialRelativeParts.current;
Expand Down Expand Up @@ -115,6 +121,7 @@ export const EuiRelativeTab: FunctionComponent<EuiRelativeTabProps> = ({
}, [isInvalid, value, roundUp, locale, dateFormat]);

const relativeDateInputNumberDescriptionId = useGeneratedHtmlId();
const timeZomeDescriptionId = useGeneratedHtmlId();
const numberAriaLabel = useEuiI18n(
'euiRelativeTab.numberInputLabel',
'Time span amount'
Expand Down Expand Up @@ -174,6 +181,7 @@ export const EuiRelativeTab: FunctionComponent<EuiRelativeTabProps> = ({
compressed
value={formattedValue}
readOnly
aria-describedby={timeZomeDescriptionId}
prepend={<EuiFormLabel>{labelPrefix}</EuiFormLabel>}
/>
<EuiScreenReaderOnly>
Expand All @@ -186,6 +194,10 @@ export const EuiRelativeTab: FunctionComponent<EuiRelativeTabProps> = ({
</p>
</EuiScreenReaderOnly>
</EuiForm>
<EuiTimeZoneDisplay
id={timeZomeDescriptionId}
{...timeZoneDisplayProps}
/>
<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,80 @@
/*
* 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 timeZone="America/Los_Angeles" />
);

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

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

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

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

it('handles "UTC" time zone name', () => {
const { getByTestSubject } = render(<EuiTimeZoneDisplay timeZone="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 timeZone="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
timeZone="Europe/Helsinki"
customRender={({ nameDisplay }) => (
<>
{nameDisplay}
{customContent}
</>
)}
/>
);

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