diff --git a/CHANGELOG.md b/CHANGELOG.md
index 58fd7d5a89d4..c878813e9c0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,6 +48,7 @@ All notable changes to this project will be documented in this file.
- Fixed issue with timestamps being rendered incorrectly in segment menus and modals for some timezones
- Fixed main graph being clipped when the browser's root font size is smaller than the default 16px
- Fixed issue with users with billing role not being able to create personal segments
+- Fixed period arrow keys hijacking custom-range calendar
## v3.2.0 - 2026-01-16
diff --git a/assets/js/dashboard/keybinding.tsx b/assets/js/dashboard/keybinding.tsx
index 646b35bd206c..7fa707d88432 100644
--- a/assets/js/dashboard/keybinding.tsx
+++ b/assets/js/dashboard/keybinding.tsx
@@ -113,11 +113,13 @@ export function Keybind(opts: KeybindOptions) {
export function NavigateKeybind({
keyboardKey,
type,
- navigateProps
+ navigateProps,
+ shouldAlsoIgnoreWhen = []
}: {
keyboardKey: string
type: KeyboardEventType
navigateProps: AppNavigationTarget
+ shouldAlsoIgnoreWhen?: Array<(event: KeyboardEvent) => boolean>
}) {
const navigate = useAppNavigate()
const handler = useCallback(() => {
@@ -129,7 +131,7 @@ export function NavigateKeybind({
keyboardKey={keyboardKey}
type={type}
handler={handler}
- shouldIgnoreWhen={[isModifierPressed, isTyping]}
+ shouldIgnoreWhen={[isModifierPressed, isTyping, ...shouldAlsoIgnoreWhen]}
targetRef="document"
/>
)
diff --git a/assets/js/dashboard/nav-menu/query-periods/dashboard-dates.test.tsx b/assets/js/dashboard/nav-menu/query-periods/dashboard-dates.test.tsx
index 64d3eca180ff..f57d6b80d552 100644
--- a/assets/js/dashboard/nav-menu/query-periods/dashboard-dates.test.tsx
+++ b/assets/js/dashboard/nav-menu/query-periods/dashboard-dates.test.tsx
@@ -150,6 +150,36 @@ test.each([
}
)
+test('arrow keys shift the period when the custom-range calendar is closed, but not while it is open', async () => {
+ const localDomain = 'no-arrow-hijack.test'
+ const startUrl = `${getRouterBasepath({ domain: localDomain, shared: false })}${stringifySearch({ period: 'month', date: '2024-08-15' })}`
+
+ render(, {
+ wrapper: (props) => (
+
+ )
+ })
+
+ expect(screen.getByText('August 2024')).toBeVisible()
+ expect(document.querySelector('.flatpickr-calendar')).toBeNull()
+
+ await userEvent.keyboard('{ArrowLeft}')
+ expect(screen.getByText('July 2024')).toBeVisible()
+
+ await userEvent.keyboard('c')
+ expect(document.querySelector('.flatpickr-calendar')).not.toBeNull()
+
+ await userEvent.keyboard('{ArrowLeft}')
+ await userEvent.keyboard('{ArrowLeft}')
+
+ expect(screen.getByText('July 2024')).toBeVisible()
+ expect(document.querySelector('.flatpickr-calendar')).not.toBeNull()
+})
+
test('going back resets the stored dashboardState period to previous value', async () => {
const BrowserBackButton = () => {
const navigate = useNavigate()
diff --git a/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.tsx b/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.tsx
index dae81d15077c..021594b3a9a6 100644
--- a/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.tsx
+++ b/assets/js/dashboard/nav-menu/query-periods/date-range-calendar.tsx
@@ -2,6 +2,9 @@ import React, { useLayoutEffect, useRef } from 'react'
import DatePicker from 'react-flatpickr'
import { CustomLocale } from 'flatpickr/dist/types/locale'
+export const isDateRangeCalendarOpen = (): boolean =>
+ !!document.querySelector('.flatpickr-calendar')
+
const calendarLocale: Partial = {
weekdays: {
shorthand: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
diff --git a/assets/js/dashboard/nav-menu/query-periods/move-period-arrows.tsx b/assets/js/dashboard/nav-menu/query-periods/move-period-arrows.tsx
index f5aa35ce0561..bfce22d42ca3 100644
--- a/assets/js/dashboard/nav-menu/query-periods/move-period-arrows.tsx
+++ b/assets/js/dashboard/nav-menu/query-periods/move-period-arrows.tsx
@@ -11,6 +11,7 @@ import { AppNavigationLink } from '../../navigation/use-app-navigate'
import { DashboardPeriod } from '../../dashboard-time-periods'
import { useMatch } from 'react-router-dom'
import { rootRoute } from '../../router'
+import { isDateRangeCalendarOpen } from './date-range-calendar'
const ArrowKeybind = ({
keyboardKey
@@ -36,6 +37,8 @@ const ArrowKeybind = ({
type="keydown"
keyboardKey={keyboardKey}
navigateProps={{ search }}
+ // Don't hijack arrow keys flatpickr uses for its own day-cell navigation.
+ shouldAlsoIgnoreWhen={[isDateRangeCalendarOpen]}
/>
)
}