diff --git a/frontend/__snapshots__/scenes-app-recordings--recordings-play-list-no-pinned-recordings.png b/frontend/__snapshots__/scenes-app-recordings--recordings-play-list-no-pinned-recordings.png
index b79c4b316f022..0dd5b12b4fe35 100644
Binary files a/frontend/__snapshots__/scenes-app-recordings--recordings-play-list-no-pinned-recordings.png and b/frontend/__snapshots__/scenes-app-recordings--recordings-play-list-no-pinned-recordings.png differ
diff --git a/frontend/src/lib/components/TZLabel/index.scss b/frontend/src/lib/components/TZLabel/index.scss
index d987e42d411e5..cdfb2645b50d6 100644
--- a/frontend/src/lib/components/TZLabel/index.scss
+++ b/frontend/src/lib/components/TZLabel/index.scss
@@ -1,38 +1,27 @@
-.tz-label {
- white-space: nowrap;
+.TZLabelPopover {
+ .TZLabelPopover__row {
+ display: flex;
+ margin-top: 0.5rem;
- &.tz-label--hoverable {
- border-bottom: 1px dotted var(--border-bold);
- cursor: default;
- }
-}
+ > :nth-child(1) {
+ font-weight: bold;
+ color: var(--primary-alt);
+ margin-right: 6px;
+ }
-.tz-label-popover {
- .divider {
- margin-right: -4px;
- margin-left: -4px;
- border-bottom: 1px solid var(--border-light);
- }
- .timezones {
- .timezone {
- display: flex;
- margin-top: 8px;
- .name {
- font-weight: bold;
- color: var(--primary-alt);
- margin-right: 6px;
- }
+ > :nth-child(2) {
+ color: var(--muted);
+ margin-right: 16px;
+ flex-grow: 1;
+ font-style: italic;
+ }
- .scope {
- color: var(--muted);
- margin-right: 16px;
- flex-grow: 1;
- font-style: italic;
- }
+ > :nth-child(3) {
+ min-width: 10rem;
+ text-align: right;
- .time {
- min-width: 170px;
- text-align: right;
+ .TZLabelPopover--seconds & {
+ min-width: 12rem;
}
}
}
diff --git a/frontend/src/lib/components/TZLabel/index.tsx b/frontend/src/lib/components/TZLabel/index.tsx
index 2f25a5d25bc18..a1d28b764d79e 100644
--- a/frontend/src/lib/components/TZLabel/index.tsx
+++ b/frontend/src/lib/components/TZLabel/index.tsx
@@ -1,32 +1,20 @@
import './index.scss'
-import { Col, Popover, Row } from 'antd'
+import { Popover } from 'antd'
import { useActions, useValues } from 'kea'
-import { ProjectOutlined, LaptopOutlined, GlobalOutlined, SettingOutlined } from '@ant-design/icons'
-import { Link } from 'lib/lemon-ui/Link'
+import { ProjectOutlined, LaptopOutlined, GlobalOutlined } from '@ant-design/icons'
import { humanFriendlyDetailedTime, shortTimeZone } from 'lib/utils'
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { teamLogic } from '../../../scenes/teamLogic'
import { dayjs } from 'lib/dayjs'
-import { usePeriodicRerender } from 'lib/hooks/usePeriodicRerender'
import clsx from 'clsx'
-import React from 'react'
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { styles } from '../../../styles/vars'
+import { LemonButton, LemonDivider } from '@posthog/lemon-ui'
+import { IconSettings } from 'lib/lemon-ui/icons'
+import { urls } from 'scenes/urls'
const BASE_OUTPUT_FORMAT = 'ddd, MMM D, YYYY h:mm A'
-function TZConversionHeader(): JSX.Element {
- return (
-
- Timezone conversion
-
-
-
-
-
-
- )
-}
-
interface TZLabelRawProps {
time: string | dayjs.Dayjs
showSeconds?: boolean
@@ -37,6 +25,60 @@ interface TZLabelRawProps {
className?: string
}
+const TZLabelPopoverContent = React.memo(function TZLabelPopoverContent({
+ showSeconds,
+ time,
+}: Pick & { time: dayjs.Dayjs }): JSX.Element {
+ const DATE_OUTPUT_FORMAT = !showSeconds ? BASE_OUTPUT_FORMAT : `${BASE_OUTPUT_FORMAT}:ss`
+ const { currentTeam } = useValues(teamLogic)
+ const { reportTimezoneComponentViewed } = useActions(eventUsageLogic)
+
+ useEffect(() => {
+ reportTimezoneComponentViewed('label', currentTeam?.timezone, shortTimeZone())
+ }, [])
+
+ return (
+
+
+
Timezone conversion
+
+ } size="small" to={urls.projectSettings('timezone')} />
+
+
+
+
+
+
+
+
+ {shortTimeZone(undefined, time.toDate())}
+
+
Your device
+
{time.format(DATE_OUTPUT_FORMAT)}
+
+ {currentTeam && (
+
+
+
{shortTimeZone(currentTeam.timezone, time.toDate())}
+
+
Project
+
{time.tz(currentTeam.timezone).format(DATE_OUTPUT_FORMAT)}
+
+ )}
+ {currentTeam?.timezone !== 'UTC' && (
+
+
+ UTC
+
+
+
{time.tz('UTC').format(DATE_OUTPUT_FORMAT)}
+
+ )}
+
+
+ )
+})
+
/** Return a simple label component with timezone conversion UI. */
function TZLabelRaw({
time,
@@ -47,73 +89,42 @@ function TZLabelRaw({
noStyles = false,
className,
}: TZLabelRawProps): JSX.Element {
- usePeriodicRerender(1000)
+ const parsedTime = useMemo(() => (dayjs.isDayjs(time) ? time : dayjs(time)), [time])
- const parsedTime = dayjs.isDayjs(time) ? time : dayjs(time)
- const { currentTeam } = useValues(teamLogic)
+ const format = useCallback(() => {
+ return formatDate || formatTime
+ ? humanFriendlyDetailedTime(parsedTime, formatDate, formatTime)
+ : parsedTime.fromNow()
+ }, [formatDate, formatTime, parsedTime])
- const DATE_OUTPUT_FORMAT = !showSeconds ? BASE_OUTPUT_FORMAT : `${BASE_OUTPUT_FORMAT}:ss`
- const timeStyle = showSeconds ? { minWidth: 192 } : undefined
+ const [formattedContent, setFormattedContent] = useState(format())
- const { reportTimezoneComponentViewed } = useActions(eventUsageLogic)
+ useEffect(() => {
+ // NOTE: This is an optimization to make sure we don't needlessly re-render the component every second.
+ const interval = setInterval(() => {
+ if (format() !== formattedContent) {
+ setFormattedContent(format())
+ }
+ }, 1000)
+ return () => clearInterval(interval)
+ }, [parsedTime, format])
const innerContent = (
-
- {formatDate || formatTime
- ? humanFriendlyDetailedTime(parsedTime, formatDate, formatTime)
- : parsedTime.fromNow()}
+
+ {formattedContent}
)
if (showPopover) {
- const handleVisibleChange = (visible: boolean): void => {
- if (visible) {
- reportTimezoneComponentViewed('label', currentTeam?.timezone, shortTimeZone())
- }
- }
-
- const PopoverContent = (
-
-
-
-
-
-
- {shortTimeZone(undefined, parsedTime.toDate())}
-
- Your device
-
- {parsedTime.format(DATE_OUTPUT_FORMAT)}
-
-
- {currentTeam && (
-
-
- {shortTimeZone(currentTeam.timezone, parsedTime.toDate())}
-
- Project
-
- {parsedTime.tz(currentTeam.timezone).format(DATE_OUTPUT_FORMAT)}
-
-
- )}
- {currentTeam?.timezone !== 'UTC' && (
-
-
- UTC
-
-
-
- {parsedTime.tz('UTC').format(DATE_OUTPUT_FORMAT)}
-
-
- )}
-
-
- )
-
return (
-
+ }
+ zIndex={styles.zPopover}
+ >
{innerContent}
)
diff --git a/playwright/e2e-vrt/layout/Navigation.spec.ts-snapshots/Navigation-App-Page-With-Side-Bar-Hidden-Mobile-1-chromium-linux.png b/playwright/e2e-vrt/layout/Navigation.spec.ts-snapshots/Navigation-App-Page-With-Side-Bar-Hidden-Mobile-1-chromium-linux.png
index 86b5a55ad5b75..24af8b2279910 100644
Binary files a/playwright/e2e-vrt/layout/Navigation.spec.ts-snapshots/Navigation-App-Page-With-Side-Bar-Hidden-Mobile-1-chromium-linux.png and b/playwright/e2e-vrt/layout/Navigation.spec.ts-snapshots/Navigation-App-Page-With-Side-Bar-Hidden-Mobile-1-chromium-linux.png differ