From 956a6dcd61eed2d363ce1ce150a39072fe51936e Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Thu, 12 Feb 2026 00:14:09 +0100 Subject: [PATCH 01/51] Add helper for adding custom goal and use it in a new test --- e2e/tests/fixtures.ts | 44 +++++++++++++++++++++++++++++++++++++++ e2e/tests/general.spec.ts | 34 +++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/e2e/tests/fixtures.ts b/e2e/tests/fixtures.ts index c1c23ddf93d8..08ab055cd2c8 100644 --- a/e2e/tests/fixtures.ts +++ b/e2e/tests/fixtures.ts @@ -15,6 +15,7 @@ type EventTimestamp = type Event = { name: string + user_id?: number pathname?: string timestamp?: EventTimestamp } @@ -152,6 +153,49 @@ export async function populateStats({ expect(response.ok()).toBeTruthy() } +export async function addCustomGoal({ + page, + domain, + name, + displayName +}: { + page: Page + domain: string + name: string + displayName: string +}) { + await page.goto(`/${domain}/settings/goals`) + + await expectLiveViewConnected(page) + + await page.getByRole('button', { name: 'Add goal' }).click() + const customEventButton = page.locator( + 'button[phx-value-goal-type="custom_events"]' + ) + await expect(customEventButton).toBeVisible() + await customEventButton.click() + const addManuallyButton = page.getByRole('button', { name: 'Add manually' }) + await expect(addManuallyButton).toBeVisible() + await addManuallyButton.click() + await expect( + page.getByRole('heading', { name: `Add goal for ${domain}` }) + ).toBeVisible() + // NOTE: Locating inputs by role and label does not work in this case + // for some reason. + const nameInput = page.locator('input[placeholder="e.g. Signup"]') + await nameInput.fill(name) + await page.locator(`a[data-display-value="${name}"]`).click() + await expect(nameInput).toHaveAttribute('value', name) + await page.locator('input#custom_event_display_name_input').fill(displayName) + + await page + .locator('form[phx-submit="save-goal"]') + .getByRole('button', { name: 'Add goal' }) + .click() + + await expect(page.locator('body')).toContainText('Goal saved successfully') +} + export async function setupSite({ page, request diff --git a/e2e/tests/general.spec.ts b/e2e/tests/general.spec.ts index 7aee23d46b1d..7063f2b5e63c 100644 --- a/e2e/tests/general.spec.ts +++ b/e2e/tests/general.spec.ts @@ -1,5 +1,11 @@ import { test, expect } from '@playwright/test' -import { setupSite, logout, makeSitePublic, populateStats } from './fixtures.ts' +import { + setupSite, + logout, + makeSitePublic, + populateStats, + addCustomGoal +} from './fixtures.ts' test('dashboard renders for logged in user', async ({ page, request }) => { const { domain } = await setupSite({ page, request }) @@ -165,3 +171,29 @@ test('back navigation closes the modal', async ({ page, request, baseURL }) => { await expect(page).toHaveURL(baseURL + '/' + domain) }) + +test('goals are rendered', async ({ page, request }) => { + const { domain } = await setupSite({ page, request }) + await populateStats({ + request, + domain, + events: [ + { user_id: 123, name: 'pageview', timestamp: { minutesAgo: 60 } }, + { user_id: 123, name: 'add_site', timestamp: { minutesAgo: 50 } } + ] + }) + + await addCustomGoal({ + page, + domain, + name: 'add_site', + displayName: 'Add a site' + }) + + await page.goto('/' + domain) + + await expect(page.getByRole('button', { name: domain })).toBeVisible() + // To ensure lazy loading of behaviours is triggered + page.getByRole('button', { name: 'Goals' }).scrollIntoViewIfNeeded() + await expect(page.getByRole('link', { name: 'Add a site' })).toBeVisible() +}) From adfda522a3fb95053a24a964810fcec9a1734040 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Thu, 12 Feb 2026 12:43:49 +0100 Subject: [PATCH 02/51] Extend and add goal helpers to support all goal types --- e2e/tests/fixtures.ts | 131 +++++++++++++++++- e2e/tests/general.spec.ts | 51 ++++++- .../support/dev/controllers/e2e_controller.ex | 3 + 3 files changed, 177 insertions(+), 8 deletions(-) diff --git a/e2e/tests/fixtures.ts b/e2e/tests/fixtures.ts index 08ab055cd2c8..71753eb5de7a 100644 --- a/e2e/tests/fixtures.ts +++ b/e2e/tests/fixtures.ts @@ -16,6 +16,9 @@ type EventTimestamp = type Event = { name: string user_id?: number + scroll_depth?: number + revenue_reporting_amount?: string + revenue_reporting_currency?: string pathname?: string timestamp?: EventTimestamp } @@ -157,12 +160,17 @@ export async function addCustomGoal({ page, domain, name, - displayName + displayName, + currency, + // Useful when adding a goal for which there's no matching stat yet + clickManually = true }: { page: Page domain: string name: string - displayName: string + displayName?: string + currency?: string + clickManually?: boolean }) { await page.goto(`/${domain}/settings/goals`) @@ -174,9 +182,11 @@ export async function addCustomGoal({ ) await expect(customEventButton).toBeVisible() await customEventButton.click() - const addManuallyButton = page.getByRole('button', { name: 'Add manually' }) - await expect(addManuallyButton).toBeVisible() - await addManuallyButton.click() + + if (clickManually) { + await page.getByRole('button', { name: 'Add manually' }).click() + } + await expect( page.getByRole('heading', { name: `Add goal for ${domain}` }) ).toBeVisible() @@ -186,7 +196,116 @@ export async function addCustomGoal({ await nameInput.fill(name) await page.locator(`a[data-display-value="${name}"]`).click() await expect(nameInput).toHaveAttribute('value', name) - await page.locator('input#custom_event_display_name_input').fill(displayName) + + if (displayName) { + await page + .locator('input#custom_event_display_name_input') + .fill(displayName) + } + + if (currency) { + page.locator('button[aria-labelledby="enable-revenue-tracking"]').click() + const currencyInput = page.locator('input[id^=currency_input_]') + await currencyInput.fill(currency) + await page.locator(`a[phx-value-submit-value="${currency}"]`).click() + await expect(page.locator('input[name="goal[currency]"]')).toHaveAttribute( + 'value', + currency + ) + } + + await page + .locator('form[phx-submit="save-goal"]') + .getByRole('button', { name: 'Add goal' }) + .click() + + await expect(page.locator('body')).toContainText('Goal saved successfully') +} + +export async function addPageviewGoal({ + page, + domain, + pathname, + displayName +}: { + page: Page + domain: string + pathname: string + displayName?: string +}) { + await page.goto(`/${domain}/settings/goals`) + + await expectLiveViewConnected(page) + + await page.getByRole('button', { name: 'Add goal' }).click() + const pageviewEventButton = page.locator( + 'button[phx-value-goal-type="pageviews"]' + ) + await expect(pageviewEventButton).toBeVisible() + await pageviewEventButton.click() + + await expect( + page.getByRole('heading', { name: `Add goal for ${domain}` }) + ).toBeVisible() + + const pathnameInput = page.locator('input[id^="page_path_input"]') + await pathnameInput.fill(pathname) + await page.locator(`a[data-display-value="${pathname}"]`).click() + await expect(pathnameInput).toHaveAttribute('value', pathname) + if (displayName) { + await page.locator('input#pageview_display_name_input').fill(displayName) + } + + await page + .locator('form[phx-submit="save-goal"]') + .getByRole('button', { name: 'Add goal' }) + .click() + + await expect(page.locator('body')).toContainText('Goal saved successfully') +} + +export async function addScrollDepthGoal({ + page, + domain, + pathname, + displayName, + scrollPercentage +}: { + page: Page + domain: string + pathname: string + displayName?: string + scrollPercentage?: number +}) { + await page.goto(`/${domain}/settings/goals`) + + await expectLiveViewConnected(page) + + await page.getByRole('button', { name: 'Add goal' }).click() + const scrollDepthEventButton = page.locator( + 'button[phx-value-goal-type="scroll"]' + ) + await expect(scrollDepthEventButton).toBeVisible() + await scrollDepthEventButton.click() + + await expect( + page.getByRole('heading', { name: `Add goal for ${domain}` }) + ).toBeVisible() + + if (scrollPercentage) { + await page + .locator('input[name="goal[scroll_threshold]"]') + .fill(scrollPercentage.toString()) + } + + const pathnameInput = page.locator('input[id^="scroll_page_path_input"]') + await pathnameInput.fill(pathname) + await page.locator(`a[data-display-value="${pathname}"]`).click() + await expect(pathnameInput).toHaveAttribute('value', pathname) + + if (displayName) { + await page.locator('input#scroll_display_name_input').fill(displayName) + } await page .locator('form[phx-submit="save-goal"]') diff --git a/e2e/tests/general.spec.ts b/e2e/tests/general.spec.ts index 7063f2b5e63c..7796233f6ea7 100644 --- a/e2e/tests/general.spec.ts +++ b/e2e/tests/general.spec.ts @@ -4,7 +4,9 @@ import { logout, makeSitePublic, populateStats, - addCustomGoal + addCustomGoal, + addPageviewGoal, + addScrollDepthGoal } from './fixtures.ts' test('dashboard renders for logged in user', async ({ page, request }) => { @@ -178,7 +180,27 @@ test('goals are rendered', async ({ page, request }) => { request, domain, events: [ - { user_id: 123, name: 'pageview', timestamp: { minutesAgo: 60 } }, + { + user_id: 123, + name: 'pageview', + pathname: '/page1', + timestamp: { minutesAgo: 60 } + }, + { + user_id: 123, + name: 'engagement', + pathname: '/page1', + scroll_depth: 80, + timestamp: { minutesAgo: 59 } + }, + { + user_id: 123, + name: 'purchase', + pathname: '/buy', + revenue_reporting_amount: '23', + revenue_reporting_currency: 'EUR', + timestamp: { minutesAgo: 59 } + }, { user_id: 123, name: 'add_site', timestamp: { minutesAgo: 50 } } ] }) @@ -190,10 +212,35 @@ test('goals are rendered', async ({ page, request }) => { displayName: 'Add a site' }) + await addCustomGoal({ + page, + domain, + name: 'purchase', + currency: 'EUR' + }) + + await addPageviewGoal({ + page, + domain, + pathname: '/page1' + }) + + await addScrollDepthGoal({ + page, + domain, + pathname: '/page1', + scrollPercentage: 75 + }) + await page.goto('/' + domain) await expect(page.getByRole('button', { name: domain })).toBeVisible() // To ensure lazy loading of behaviours is triggered page.getByRole('button', { name: 'Goals' }).scrollIntoViewIfNeeded() await expect(page.getByRole('link', { name: 'Add a site' })).toBeVisible() + await expect(page.getByRole('link', { name: 'Visit /page1' })).toBeVisible() + await expect(page.getByRole('link', { name: 'purchase' })).toBeVisible() + await expect( + page.getByRole('link', { name: 'Scroll 75% on /page1' }) + ).toBeVisible() }) diff --git a/test/support/dev/controllers/e2e_controller.ex b/test/support/dev/controllers/e2e_controller.ex index b0246f4ab8b6..f7bc04671cfe 100644 --- a/test/support/dev/controllers/e2e_controller.ex +++ b/test/support/dev/controllers/e2e_controller.ex @@ -29,6 +29,9 @@ defmodule PlausibleWeb.E2EController do {"timestamp", value} -> {:timestamp, to_timestamp(value)} + {"revenue_reporting_amount", value} -> + {:revenue_reporting_amount, Decimal.new(value)} + {key, value} -> {String.to_existing_atom(key), value} end) From 0ff7322a1e0a47514d7965fe79007945fab5b933 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Thu, 12 Feb 2026 16:02:28 +0100 Subject: [PATCH 03/51] Add intro tests --- .../introduction-to-the-dashboard.spec.ts | 200 ++++++++++++++++++ .../support/dev/controllers/e2e_controller.ex | 6 +- 2 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 e2e/tests/dashboard/introduction-to-the-dashboard.spec.ts diff --git a/e2e/tests/dashboard/introduction-to-the-dashboard.spec.ts b/e2e/tests/dashboard/introduction-to-the-dashboard.spec.ts new file mode 100644 index 000000000000..956e9edbc78c --- /dev/null +++ b/e2e/tests/dashboard/introduction-to-the-dashboard.spec.ts @@ -0,0 +1,200 @@ +import { test, expect } from '@playwright/test' +import { ZonedDateTime, ZoneOffset, ChronoUnit } from '@js-joda/core' +import { setupSite, populateStats } from '../fixtures.ts' + +test('top stats show relevant metrics', async ({ page, request }) => { + const { domain } = await setupSite({ page, request }) + await populateStats({ + request, + domain, + events: [ + { + user_id: 123, + name: 'pageview', + pathname: '/', + timestamp: { minutesAgo: 120 } + }, + { + user_id: 123, + name: 'pageview', + pathname: '/', + timestamp: { minutesAgo: 60 } + }, + { + user_id: 123, + name: 'pageview', + pathname: '/page1', + timestamp: { minutesAgo: 50 } + }, + { + user_id: 456, + name: 'pageview', + pathname: '/', + timestamp: { minutesAgo: 80 } + } + ] + }) + + await page.goto('/' + domain) + + await expect(page).toHaveTitle(/Plausible/) + + await expect(page.getByRole('button', { name: domain })).toBeVisible() + + await expect(page.locator('#visitors')).toHaveText('2') + await expect(page.locator('#visits')).toHaveText('3') + await expect(page.locator('#pageviews')).toHaveText('4') + await expect(page.locator('#views_per_visit')).toHaveText('1.33') + await expect(page.locator('#bounce_rate')).toHaveText('67%') + await expect(page.locator('#visit_duration')).toHaveText('3m 20s') +}) + +test.only('different time intervals are supported', async ({ + page, + request +}) => { + const now = ZonedDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS) + const startOfDay = now.truncatedTo(ChronoUnit.DAYS) + const startOfYesterday = startOfDay.minusDays(1) + const startOfMonth = startOfDay.withDayOfMonth(1) + const startOfLastMonth = startOfMonth.minusMonths(1) + const startOfYear = now.withDayOfYear(1) + + const expectedCounts = [ + { + label: 'Today', + from: startOfDay, + to: now, + key: 'd', + value: 0 + }, + { + label: null, + from: startOfYesterday, + to: startOfDay, + key: 'e', + value: 0 + }, + { + label: 'Realtime', + from: now.minusMinutes(30), + to: now, + key: 'r', + value: 0 + }, + { + label: 'Last 24 hours', + from: now.minusHours(24), + to: now, + key: 'h', + value: 0 + }, + { + label: 'Last 7 days', + from: startOfDay.minusDays(7), + to: startOfDay, + key: 'w', + value: 0 + }, + { + label: 'Last 28 days', + from: startOfDay.minusDays(28), + to: startOfDay, + key: 'f', + value: 0 + }, + { + label: 'Last 91 days', + from: startOfDay.minusDays(91), + to: startOfDay, + key: 'n', + value: 0 + }, + { + label: 'Month to Date', + from: startOfMonth, + to: now, + key: 'm', + value: 0 + }, + { + label: null, + from: startOfLastMonth, + to: startOfMonth, + key: 'p', + value: 0 + }, + { + label: 'Year to Date', + from: startOfYear, + to: now, + key: 'y', + value: 0 + }, + { + label: 'Last 12 Months', + from: startOfMonth.minusMonths(12), + to: startOfMonth, + key: 'l', + value: 0 + } + ] + + const eventTimes = [ + now.minusMinutes(20), + now.minusHours(12), + now.minusHours(26), + now.minusHours(30), + now.minusHours(35), + now.minusDays(5), + now.minusDays(17), + now.minusDays(54), + now.minusDays(120), + now.minusDays(720) + ] + + const events = [] + + eventTimes.forEach((ts, idx) => { + expectedCounts.forEach((expected) => { + if (ts.compareTo(expected.from) >= 0 && ts.compareTo(expected.to) < 0) { + expected.value += 1 + } + }) + + events.push({ + user_id: idx + 1, + name: 'pageview', + timestamp: ts.toString() + }) + }) + + const { domain } = await setupSite({ page, request }) + + await populateStats({ request, domain, events }) + + await page.goto('/' + domain) + await expect(page.getByRole('button', { name: domain })).toBeVisible() + + await expect(page.getByRole('button', { name: 'Last 28 days' })).toBeVisible() + + const visitors = page.locator('#visitors') + + for (const expected of expectedCounts) { + await page.keyboard.press(expected.key) + if (expected.label) { + await expect( + page.getByRole('button', { name: expected.label }) + ).toBeVisible() + } + await expect(visitors).toHaveText(`${expected.value}`) + } + + // Realtime + await page.keyboard.press('r') + await expect(visitors).toHaveText('1') + + // All time + await page.keyboard.press('a') + await expect(visitors).toHaveText(`${events.length}`) +}) diff --git a/test/support/dev/controllers/e2e_controller.ex b/test/support/dev/controllers/e2e_controller.ex index f7bc04671cfe..b49ff6c1fba8 100644 --- a/test/support/dev/controllers/e2e_controller.ex +++ b/test/support/dev/controllers/e2e_controller.ex @@ -11,7 +11,7 @@ defmodule PlausibleWeb.E2EController do |> Enum.map(&deserialize/1) |> Enum.map(&build/1) - stats_start_time = Enum.min_by(events, & &1.timestamp).timestamp + stats_start_time = Enum.min_by(events, & &1.timestamp, NaiveDateTime).timestamp stats_start_date = NaiveDateTime.to_date(stats_start_time) site @@ -65,4 +65,8 @@ defmodule PlausibleWeb.E2EController do defp to_timestamp(%{"minutesAgo" => offset}) do NaiveDateTime.utc_now(:second) |> NaiveDateTime.add(-offset, :minute) end + + defp to_timestamp(ts) when is_binary(ts) do + NaiveDateTime.from_iso8601!(ts) + end end From 3f4c968359c3a1d0d4536b4b9ad2e2333ba935fa Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Thu, 12 Feb 2026 22:15:00 +0100 Subject: [PATCH 04/51] Add js-joda for time manipulation in tests --- e2e/package-lock.json | 8 ++++++++ e2e/package.json | 1 + 2 files changed, 9 insertions(+) diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 8a2e740f9f6e..35bd687223f3 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -9,6 +9,7 @@ "license": "MIT", "devDependencies": { "@eslint/js": "^9.39.2", + "@js-joda/core": "^5.7.0", "@playwright/test": "^1.58.0", "@types/node": "^25.1.0", "eslint": "^9.39.2", @@ -215,6 +216,13 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@js-joda/core": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.7.0.tgz", + "integrity": "sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@playwright/test": { "version": "1.58.0", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.0.tgz", diff --git a/e2e/package.json b/e2e/package.json index 73aaae00a2af..6f8a27ce5e83 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -9,6 +9,7 @@ "license": "MIT", "devDependencies": { "@eslint/js": "^9.39.2", + "@js-joda/core": "^5.7.0", "@playwright/test": "^1.58.0", "@types/node": "^25.1.0", "eslint": "^9.39.2", From ad2819f2217e52c409d7fc6ed5216d43b9ce63ca Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Mon, 16 Feb 2026 10:58:09 +0100 Subject: [PATCH 05/51] Properly test for different time ranges --- .../introduction-to-the-dashboard.spec.ts | 98 +++---------------- 1 file changed, 12 insertions(+), 86 deletions(-) diff --git a/e2e/tests/dashboard/introduction-to-the-dashboard.spec.ts b/e2e/tests/dashboard/introduction-to-the-dashboard.spec.ts index 956e9edbc78c..9e3d1291123a 100644 --- a/e2e/tests/dashboard/introduction-to-the-dashboard.spec.ts +++ b/e2e/tests/dashboard/introduction-to-the-dashboard.spec.ts @@ -49,10 +49,7 @@ test('top stats show relevant metrics', async ({ page, request }) => { await expect(page.locator('#visit_duration')).toHaveText('3m 20s') }) -test.only('different time intervals are supported', async ({ - page, - request -}) => { +test('different time ranges are supported', async ({ page, request }) => { const now = ZonedDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS) const startOfDay = now.truncatedTo(ChronoUnit.DAYS) const startOfYesterday = startOfDay.minusDays(1) @@ -61,83 +58,17 @@ test.only('different time intervals are supported', async ({ const startOfYear = now.withDayOfYear(1) const expectedCounts = [ - { - label: 'Today', - from: startOfDay, - to: now, - key: 'd', - value: 0 - }, - { - label: null, - from: startOfYesterday, - to: startOfDay, - key: 'e', - value: 0 - }, - { - label: 'Realtime', - from: now.minusMinutes(30), - to: now, - key: 'r', - value: 0 - }, - { - label: 'Last 24 hours', - from: now.minusHours(24), - to: now, - key: 'h', - value: 0 - }, - { - label: 'Last 7 days', - from: startOfDay.minusDays(7), - to: startOfDay, - key: 'w', - value: 0 - }, - { - label: 'Last 28 days', - from: startOfDay.minusDays(28), - to: startOfDay, - key: 'f', - value: 0 - }, - { - label: 'Last 91 days', - from: startOfDay.minusDays(91), - to: startOfDay, - key: 'n', - value: 0 - }, - { - label: 'Month to Date', - from: startOfMonth, - to: now, - key: 'm', - value: 0 - }, - { - label: null, - from: startOfLastMonth, - to: startOfMonth, - key: 'p', - value: 0 - }, - { - label: 'Year to Date', - from: startOfYear, - to: now, - key: 'y', - value: 0 - }, - { - label: 'Last 12 Months', - from: startOfMonth.minusMonths(12), - to: startOfMonth, - key: 'l', - value: 0 - } + { from: startOfDay, to: now, key: 'd', value: 0 }, + { from: startOfYesterday, to: startOfDay, key: 'e', value: 0 }, + { from: now.minusMinutes(30), to: now, key: 'r', value: 0 }, + { from: now.minusHours(24), to: now, key: 'h', value: 0 }, + { from: startOfDay.minusDays(7), to: startOfDay, key: 'w', value: 0 }, + { from: startOfDay.minusDays(28), to: startOfDay, key: 'f', value: 0 }, + { from: startOfDay.minusDays(91), to: startOfDay, key: 'n', value: 0 }, + { from: startOfMonth, to: now, key: 'm', value: 0 }, + { from: startOfLastMonth, to: startOfMonth, key: 'p', value: 0 }, + { from: startOfYear, to: now, key: 'y', value: 0 }, + { from: startOfMonth.minusMonths(12), to: startOfMonth, key: 'l', value: 0 } ] const eventTimes = [ @@ -182,11 +113,6 @@ test.only('different time intervals are supported', async ({ for (const expected of expectedCounts) { await page.keyboard.press(expected.key) - if (expected.label) { - await expect( - page.getByRole('button', { name: expected.label }) - ).toBeVisible() - } await expect(visitors).toHaveText(`${expected.value}`) } From 16343b217a13352e4992bb9bb92969579d1afd39 Mon Sep 17 00:00:00 2001 From: Adrian Gruntkowski Date: Mon, 16 Feb 2026 12:38:11 +0100 Subject: [PATCH 06/51] Test graph intervals --- .../query-periods/dashboard-period-menu.tsx | 5 +- .../query-periods/dashboard-period-picker.tsx | 5 +- .../dashboard/stats/graph/interval-picker.tsx | 8 +++- .../introduction-to-the-dashboard.spec.ts | 48 ++++++++++++++++++- 4 files changed, 61 insertions(+), 5 deletions(-) diff --git a/assets/js/dashboard/nav-menu/query-periods/dashboard-period-menu.tsx b/assets/js/dashboard/nav-menu/query-periods/dashboard-period-menu.tsx index 99b8edd22098..e5fd6afcd26d 100644 --- a/assets/js/dashboard/nav-menu/query-periods/dashboard-period-menu.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/dashboard-period-menu.tsx @@ -96,7 +96,10 @@ export const DashboardPeriodMenu = ({ <> - + {getCurrentPeriodDisplayName({ dashboardState, site })} diff --git a/assets/js/dashboard/nav-menu/query-periods/dashboard-period-picker.tsx b/assets/js/dashboard/nav-menu/query-periods/dashboard-period-picker.tsx index 38caa5b876c7..aceb3d3f2359 100644 --- a/assets/js/dashboard/nav-menu/query-periods/dashboard-period-picker.tsx +++ b/assets/js/dashboard/nav-menu/query-periods/dashboard-period-picker.tsx @@ -17,7 +17,10 @@ export function DashboardPeriodPicker({ className }: { className?: string }) { const compareCalendarButtonRef = useRef(null) return ( -
+
{({ close }) => ( diff --git a/assets/js/dashboard/stats/graph/interval-picker.tsx b/assets/js/dashboard/stats/graph/interval-picker.tsx index aadc285ac692..d53b20d6d532 100644 --- a/assets/js/dashboard/stats/graph/interval-picker.tsx +++ b/assets/js/dashboard/stats/graph/interval-picker.tsx @@ -154,7 +154,9 @@ export function IntervalPicker({ 'rounded-sm text-sm flex items-center' )} > - {INTERVAL_LABELS[currentInterval]} + + {INTERVAL_LABELS[currentInterval]} +