diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts index 94512cf950a76..908be67cb6cab 100644 --- a/.storybook/test-runner.ts +++ b/.storybook/test-runner.ts @@ -1,5 +1,5 @@ import { toMatchImageSnapshot } from 'jest-image-snapshot' -import { getStoryContext, TestRunnerConfig, TestContext } from '@storybook/test-runner' +import { getStoryContext, TestRunnerConfig, TestContext, waitForPageReady } from '@storybook/test-runner' import type { Locator, Page, LocatorScreenshotOptions } from '@playwright/test' import type { Mocks } from '~/mocks/utils' import { StoryContext } from '@storybook/types' @@ -110,8 +110,7 @@ async function expectStoryToMatchSnapshot( check = expectStoryToMatchComponentSnapshot } - // Wait for story to load - await page.waitForSelector('.sb-show-preparing-story', { state: 'detached' }) + await waitForPageReady(page) await page.evaluate(() => { // Stop all animations for consistent snapshots document.body.classList.add('storybook-test-runner') @@ -123,7 +122,7 @@ async function expectStoryToMatchSnapshot( await page.waitForSelector(waitForSelector) } - await page.waitForTimeout(400) // Wait for animations to finish + await page.waitForTimeout(400) // Wait for effects to finish // Wait for all images to load await page.waitForFunction(() => diff --git a/frontend/__snapshots__/filters-cohort-filters-fields-number--basic.png b/frontend/__snapshots__/filters-cohort-filters-fields-number--basic.png index f8cb1193d87e2..d9d0a7dd0ada5 100644 Binary files a/frontend/__snapshots__/filters-cohort-filters-fields-number--basic.png and b/frontend/__snapshots__/filters-cohort-filters-fields-number--basic.png differ diff --git a/frontend/__snapshots__/filters-propertyfilters--comparing-property-filters.png b/frontend/__snapshots__/filters-propertyfilters--comparing-property-filters.png index 4bf204ad1e606..d90e64ea54ca3 100644 Binary files a/frontend/__snapshots__/filters-propertyfilters--comparing-property-filters.png and b/frontend/__snapshots__/filters-propertyfilters--comparing-property-filters.png differ diff --git a/frontend/__snapshots__/filters-propertyfilters--with-no-close-button.png b/frontend/__snapshots__/filters-propertyfilters--with-no-close-button.png index d6139bbdf9627..511e5bcc051b4 100644 Binary files a/frontend/__snapshots__/filters-propertyfilters--with-no-close-button.png and b/frontend/__snapshots__/filters-propertyfilters--with-no-close-button.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--custom-styles.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--custom-styles.png index bc7634e3b5f1e..92882cab397a8 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--custom-styles.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--custom-styles.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--default.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--default.png index 3ac0dedd31030..9e1bea26011c1 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--default.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--default.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--friday-first.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--friday-first.png index 8cc57d7815d4b..751a6708b994f 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--friday-first.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--friday-first.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--monday-first.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--monday-first.png index 0e49faf8d2454..c2af14074c91a 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--monday-first.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--monday-first.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--multiple-months.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--multiple-months.png index f17a82b5c17f5..a79d0f0a89777 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--multiple-months.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--multiple-months.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--saturday-first.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--saturday-first.png index d22c3d4b650b3..5a0879568f37e 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--saturday-first.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--saturday-first.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--sunday-first.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--sunday-first.png index 3ac0dedd31030..9e1bea26011c1 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--sunday-first.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--sunday-first.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--thursday-first.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--thursday-first.png index 2daa09266b125..854876aec0706 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--thursday-first.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--thursday-first.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--tuesday-first.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--tuesday-first.png index 5035ace9f02d5..34d43dec75bca 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--tuesday-first.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--tuesday-first.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--wednesday-first.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--wednesday-first.png index 2677654470c84..d1ca9e9a54ff5 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--wednesday-first.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar--wednesday-first.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-range--lemon-calendar-range.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-range--lemon-calendar-range.png index 4c4220ef11640..b5e2113ee71af 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-range--lemon-calendar-range.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-range--lemon-calendar-range.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-range-inline--lemon-calendar-range-inline.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-range-inline--lemon-calendar-range-inline.png index 4d72144b7b205..6b9f4b17649a5 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-range-inline--lemon-calendar-range-inline.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-range-inline--lemon-calendar-range-inline.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-select--lemon-calendar-select.png b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-select--lemon-calendar-select.png index 1c7c6fa99c2ed..8ea8124973d82 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-select--lemon-calendar-select.png and b/frontend/__snapshots__/lemon-ui-lemon-calendar-lemon-calendar-select--lemon-calendar-select.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-select--long-options.png b/frontend/__snapshots__/lemon-ui-lemon-select--long-options.png index f9d8d709b0563..01c803c849fb2 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-select--long-options.png and b/frontend/__snapshots__/lemon-ui-lemon-select--long-options.png differ diff --git a/frontend/__snapshots__/lemon-ui-utilities--overview.png b/frontend/__snapshots__/lemon-ui-utilities--overview.png index 646796a14a8f2..57866050bab70 100644 Binary files a/frontend/__snapshots__/lemon-ui-utilities--overview.png and b/frontend/__snapshots__/lemon-ui-utilities--overview.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-historical-trends-edit--webkit.png b/frontend/__snapshots__/scenes-app-insights--funnel-historical-trends-edit--webkit.png index 613e89cdbc0d8..5ee7a8708613f 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-historical-trends-edit--webkit.png and b/frontend/__snapshots__/scenes-app-insights--funnel-historical-trends-edit--webkit.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown.png b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown.png index 3f300c2fb4964..f7f0a786c5d30 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown.png and b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-edit.png b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-edit.png index ad2443cc98efa..4cf0295e84e27 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-edit.png and b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-edit.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right.png b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right.png index f437b0531a705..456b5fd067465 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right.png and b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom.png b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom.png index 8760764ed3a04..1a4722fdb1a6f 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom.png and b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark.png b/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark.png index d00fa2fb6139e..fcee0abbf1035 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark.png and b/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark.png differ diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelDocs.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelDocs.tsx index e080f49c67f26..e193083feef00 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelDocs.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelDocs.tsx @@ -1,5 +1,5 @@ import { IconExternal } from '@posthog/icons' -import { LemonButton, LemonSkeleton } from '@posthog/lemon-ui' +import { LemonButton, LemonSelect, LemonSkeleton } from '@posthog/lemon-ui' import clsx from 'clsx' import { useActions, useValues } from 'kea' import { useEffect, useRef, useState } from 'react' @@ -8,6 +8,11 @@ import { themeLogic } from '../../themeLogic' import { SidePanelPaneHeader } from '../components/SidePanelPane' import { POSTHOG_WEBSITE_ORIGIN, sidePanelDocsLogic } from './sidePanelDocsLogic' +type Menu = { + name: string + url?: string +} + function SidePanelDocsSkeleton(): JSX.Element { return (
@@ -23,12 +28,50 @@ function SidePanelDocsSkeleton(): JSX.Element { ) } +function Menu({ + menu, + activeMenuName, + onChange, +}: { + menu: Menu[] + activeMenuName: string | null + onChange: (newValue: string | null) => void +}): JSX.Element { + return ( +
+ ({ label: name, value: name }))} + /> +
+ ) +} + export const SidePanelDocs = (): JSX.Element => { const { iframeSrc, currentUrl } = useValues(sidePanelDocsLogic) const { updatePath, unmountIframe, closeSidePanel, handleExternalUrl } = useActions(sidePanelDocsLogic) const ref = useRef(null) const [ready, setReady] = useState(false) const { isDarkModeOn } = useValues(themeLogic) + const [menu, setMenu] = useState(null) + const [activeMenuName, setActiveMenuName] = useState(null) + + const handleMenuChange = (newValue: string | null): void => { + const url = menu?.find(({ name }: Menu) => name === newValue)?.url + if (url) { + ref.current?.contentWindow?.postMessage( + { + type: 'navigate', + url, + }, + '*' + ) + } + } useEffect(() => { ref.current?.contentWindow?.postMessage( @@ -57,6 +100,15 @@ export const SidePanelDocs = (): JSX.Element => { handleExternalUrl(event.data.url) return } + if (event.data.type === 'docs-menu') { + setMenu(event.data.menu) + return + } + + if (event.data.type === 'docs-active-menu') { + setActiveMenuName(event.data.activeMenuName) + return + } console.warn('Unhandled iframe message from Docs:', event.data) } @@ -79,6 +131,7 @@ export const SidePanelDocs = (): JSX.Element => { return ( <> + {menu && } } diff --git a/frontend/src/lib/utils.tsx b/frontend/src/lib/utils.tsx index b37a126d607e8..302f81c2d0657 100644 --- a/frontend/src/lib/utils.tsx +++ b/frontend/src/lib/utils.tsx @@ -391,8 +391,16 @@ export function idToKey(array: Record[], keyField: string = 'id'): return object } -export function delay(ms: number): Promise { - return new Promise((resolve) => window.setTimeout(resolve, ms)) +export function delay(ms: number, signal?: AbortSignal): Promise { + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(resolve, ms) + if (signal) { + signal.addEventListener('abort', () => { + clearTimeout(timeoutId) + reject(new DOMException('Aborted', 'AbortError')) + }) + } + }) } export function clearDOMTextSelection(): void { diff --git a/frontend/src/queries/query.ts b/frontend/src/queries/query.ts index 29d08863a62c0..ded22d89a9979 100644 --- a/frontend/src/queries/query.ts +++ b/frontend/src/queries/query.ts @@ -35,7 +35,7 @@ import { isTimeToSeeDataSessionsQuery, } from './utils' -const QUERY_ASYNC_MAX_INTERVAL_SECONDS = 10 +const QUERY_ASYNC_MAX_INTERVAL_SECONDS = 5 const QUERY_ASYNC_TOTAL_POLL_SECONDS = 300 //get export context for a given query @@ -115,15 +115,9 @@ async function executeQuery( let currentDelay = 300 // start low, because all queries will take at minimum this while (performance.now() - pollStart < QUERY_ASYNC_TOTAL_POLL_SECONDS * 1000) { - await delay(currentDelay) + await delay(currentDelay, methodOptions?.signal) currentDelay = Math.min(currentDelay * 2, QUERY_ASYNC_MAX_INTERVAL_SECONDS * 1000) - if (methodOptions?.signal?.aborted) { - const customAbortError = new Error('Query aborted') - customAbortError.name = 'AbortError' - throw customAbortError - } - const statusResponse = await api.queryStatus.get(response.id) if (statusResponse.complete || statusResponse.error) { diff --git a/frontend/src/scenes/funnels/FunnelBarGraph/Bar.tsx b/frontend/src/scenes/funnels/FunnelBarGraph/Bar.tsx index 5b77c77556392..2569743a57205 100644 --- a/frontend/src/scenes/funnels/FunnelBarGraph/Bar.tsx +++ b/frontend/src/scenes/funnels/FunnelBarGraph/Bar.tsx @@ -25,6 +25,7 @@ interface BarProps { wrapperWidth: number } type LabelPosition = 'inside' | 'outside' + export function Bar({ percentage: conversionPercentage, name, diff --git a/frontend/src/scenes/funnels/FunnelBarGraph/FunnelBarGraph.tsx b/frontend/src/scenes/funnels/FunnelBarGraph/FunnelBarGraph.tsx index 10b44978f1cf8..77f589785120a 100644 --- a/frontend/src/scenes/funnels/FunnelBarGraph/FunnelBarGraph.tsx +++ b/frontend/src/scenes/funnels/FunnelBarGraph/FunnelBarGraph.tsx @@ -98,7 +98,7 @@ export function FunnelBarGraph({ ) : null}
-
+
{!width ? null : isBreakdown ? ( <> {step?.nested_breakdown?.map((breakdown, index) => { diff --git a/package.json b/package.json index 229bb35d6e5aa..01da1beb31545 100644 --- a/package.json +++ b/package.json @@ -295,6 +295,11 @@ "optionalDependencies": { "fsevents": "^2.3.2" }, + "pnpm": { + "overrides": { + "playwright": "1.32.2" + } + }, "lint-staged": { "*.{json,yaml,yml}": "prettier --write", "*.{css,scss}": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 619c0c789c38a..d94b6538da7a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + playwright: 1.32.2 + dependencies: '@ant-design/icons': specifier: ^4.7.0 @@ -5321,7 +5324,7 @@ packages: jest-serializer-html: 7.1.0 jest-watch-typeahead: 2.2.2(jest@29.7.0) node-fetch: 2.6.7 - playwright: 1.29.2 + playwright: 1.32.2 read-pkg-up: 7.0.1 tempy: 1.0.1 ts-dedent: 2.2.0 @@ -15410,25 +15413,19 @@ packages: find-up: 6.3.0 dev: true - /playwright-core@1.29.2: - resolution: {integrity: sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==} - engines: {node: '>=14'} - hasBin: true - dev: true - /playwright-core@1.32.2: resolution: {integrity: sha512-zD7aonO+07kOTthsrCR3YCVnDcqSHIJpdFUtZEMOb6//1Rc7/6mZDRdw+nlzcQiQltOOsiqI3rrSyn/SlyjnJQ==} engines: {node: '>=14'} hasBin: true dev: true - /playwright@1.29.2: - resolution: {integrity: sha512-hKBYJUtdmYzcjdhYDkP9WGtORwwZBBKAW8+Lz7sr0ZMxtJr04ASXVzH5eBWtDkdb0c3LLFsehfPBTRfvlfKJOA==} + /playwright@1.32.2: + resolution: {integrity: sha512-jHVnXJke0PXpuPszKtk9y1zZSlzO5+2a+aockT/AND0oeXx46FiJEFrafthurglLygVZA+1gEbtUM1C7qtTV+Q==} engines: {node: '>=14'} hasBin: true requiresBuild: true dependencies: - playwright-core: 1.29.2 + playwright-core: 1.32.2 dev: true /please-upgrade-node@3.2.0: @@ -20010,4 +20007,4 @@ packages: /zxcvbn@4.4.2: resolution: {integrity: sha512-Bq0B+ixT/DMyG8kgX2xWcI5jUvCwqrMxSFam7m0lAf78nf04hv6lNCsyLYdyYTrCVMqNDY/206K7eExYCeSyUQ==} - dev: false \ No newline at end of file + dev: false diff --git a/posthog/admin.py b/posthog/admin.py index 2c0d26a1b01fb..7161cbaf27081 100644 --- a/posthog/admin.py +++ b/posthog/admin.py @@ -33,8 +33,6 @@ ) from posthog.warehouse.models import DataWarehouseTable -admin.site.register(DataWarehouseTable) - class DashboardTileInline(admin.TabularInline): extra = 0 @@ -81,6 +79,39 @@ def organization_link(self, dashboard: Dashboard): ) +@admin.register(DataWarehouseTable) +class DataWarehouseTableAdmin(admin.ModelAdmin): + list_display = ( + "id", + "name", + "format", + "url_pattern", + "team_link", + "organization_link", + "created_at", + "created_by", + ) + list_display_links = ("id", "name") + list_select_related = ("team", "team__organization") + search_fields = ("id", "name", "team__name", "team__organization__name") + autocomplete_fields = ("team", "created_by") + ordering = ("-created_at",) + + def team_link(self, dashboard: Dashboard): + return format_html( + '{}', + dashboard.team.pk, + dashboard.team.name, + ) + + def organization_link(self, dashboard: Dashboard): + return format_html( + '{}', + dashboard.team.organization.pk, + dashboard.team.organization.name, + ) + + @admin.register(Text) class TextAdmin(admin.ModelAdmin): autocomplete_fields = ("created_by", "last_modified_by", "team") diff --git a/posthog/warehouse/models/table.py b/posthog/warehouse/models/table.py index 2acf4fb1faf9b..93e6a20e890f2 100644 --- a/posthog/warehouse/models/table.py +++ b/posthog/warehouse/models/table.py @@ -43,6 +43,7 @@ "Array": StringArrayDatabaseField, "Map": StringJSONDatabaseField, "Bool": BooleanDatabaseField, + "Decimal": IntegerDatabaseField, } ExtractErrors = {