Limit maximum date to today in calendars (#4746)

* Limit maximum date to today in calendars

* Add test

* Make calendar max dates sensitive to site timezone
This commit is contained in:
Artur Pata 2024-11-04 11:23:36 +02:00 committed by GitHub
parent 40f28ed151
commit bfa01bff2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 112 additions and 21 deletions

View File

@ -6,7 +6,6 @@
"globals": {
"BUILD_EXTRA": true
},
"setupFiles": ["<rootDir>/test-utils/set-fixed-timezone.ts"],
"setupFilesAfterEnv": [
"<rootDir>/test-utils/extend-expect.ts",
"<rootDir>/test-utils/reset-state.ts"

View File

@ -0,0 +1,81 @@
/** @format */
import React from 'react'
import { render, screen } from '@testing-library/react'
import { DateRangeCalendar } from './date-range-calendar'
import userEvent from '@testing-library/user-event'
test('renders with default dates in view, respects max and min dates', async () => {
const onCloseWithNoSelection = jest.fn()
const onCloseWithSelection = jest.fn()
const handlers = { onCloseWithNoSelection, onCloseWithSelection }
render(
<DateRangeCalendar
minDate="2024-09-10"
maxDate="2024-09-25"
defaultDates={['2024-09-12', '2024-09-19']}
{...handlers}
/>
)
const days = await screen.queryAllByLabelText(/, 2024/)
expect(
days.map((d) => [d.getAttribute('aria-label'), d.getAttribute('class')])
).toEqual([
['September 1, 2024', 'flatpickr-day flatpickr-disabled'],
['September 2, 2024', 'flatpickr-day flatpickr-disabled'],
['September 3, 2024', 'flatpickr-day flatpickr-disabled'],
['September 4, 2024', 'flatpickr-day flatpickr-disabled'],
['September 5, 2024', 'flatpickr-day flatpickr-disabled'],
['September 6, 2024', 'flatpickr-day flatpickr-disabled'],
['September 7, 2024', 'flatpickr-day flatpickr-disabled'],
['September 8, 2024', 'flatpickr-day flatpickr-disabled'],
['September 9, 2024', 'flatpickr-day flatpickr-disabled'],
['September 10, 2024', 'flatpickr-day'],
['September 11, 2024', 'flatpickr-day'],
['September 12, 2024', 'flatpickr-day selected startRange'],
['September 13, 2024', 'flatpickr-day inRange'],
['September 14, 2024', 'flatpickr-day inRange'],
['September 15, 2024', 'flatpickr-day inRange'],
['September 16, 2024', 'flatpickr-day inRange'],
['September 17, 2024', 'flatpickr-day inRange'],
['September 18, 2024', 'flatpickr-day inRange'],
['September 19, 2024', 'flatpickr-day selected endRange'],
['September 20, 2024', 'flatpickr-day'],
['September 21, 2024', 'flatpickr-day'],
['September 22, 2024', 'flatpickr-day'],
['September 23, 2024', 'flatpickr-day'],
['September 24, 2024', 'flatpickr-day'],
['September 25, 2024', 'flatpickr-day'],
['September 26, 2024', 'flatpickr-day flatpickr-disabled'],
['September 27, 2024', 'flatpickr-day flatpickr-disabled'],
['September 28, 2024', 'flatpickr-day flatpickr-disabled'],
['September 29, 2024', 'flatpickr-day flatpickr-disabled'],
['September 30, 2024', 'flatpickr-day flatpickr-disabled'],
['October 1, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 2, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 3, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 4, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 5, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 6, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 7, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 8, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 9, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 10, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 11, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled'],
['October 12, 2024', 'flatpickr-day nextMonthDay flatpickr-disabled']
])
const newStart = await screen.getByLabelText('September 20, 2024')
await userEvent.click(newStart)
const newEnd = await screen.getByLabelText('September 25, 2024')
await userEvent.click(newEnd)
expect(onCloseWithSelection).toHaveBeenCalledTimes(1)
expect(onCloseWithSelection).toHaveBeenLastCalledWith([
new Date('2024-09-20'),
new Date('2024-09-25')
])
})

View File

@ -1,6 +1,6 @@
/* @format */
import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'
import { formatDateRange, formatISO } from './util/date'
import { formatDateRange, formatISO, nowForSite } from './util/date'
import {
shiftQueryPeriod,
getDateForShiftedPeriod,
@ -322,14 +322,16 @@ export default function QueryPeriodPicker() {
() => getCompareLinkItem({ site, query }),
[site, query]
)
const groups = useMemo(() => {
const datePeriodGroups = useMemo(() => {
const groups = getDatePeriodGroups(site)
// add Custom Range link to the last group
groups[groups.length - 1].push(customRangeLink)
if (COMPARISON_DISABLED_PERIODS.includes(query.period)) {
return groups
}
// maybe ass Compare link as another group to the very end
// maybe add Compare link as another group to the very end
return groups.concat([[compareLink]])
}, [site, query, customRangeLink, compareLink])
@ -364,7 +366,7 @@ export default function QueryPeriodPicker() {
}}
>
{menuVisible === 'datemenu' && (
<QueryPeriodsMenu groups={groups} closeMenu={closeMenu} />
<QueryPeriodsMenu groups={datePeriodGroups} closeMenu={closeMenu} />
)}
{menuVisible === 'datemenu-calendar' && (
<DateRangeCalendar
@ -372,6 +374,7 @@ export default function QueryPeriodPicker() {
navigate({ search: getSearchToApplyCustomDates(selection) })
}
minDate={site.statsBegin}
maxDate={formatISO(nowForSite(site))}
defaultDates={
query.to && query.from
? [formatISO(query.from), formatISO(query.to)]
@ -415,6 +418,7 @@ export default function QueryPeriodPicker() {
})
}
minDate={site.statsBegin}
maxDate={formatISO(nowForSite(site))}
defaultDates={
query.compare_from && query.compare_to
? [
@ -432,7 +436,7 @@ export default function QueryPeriodPicker() {
<>
<ArrowKeybind keyboardKey="ArrowLeft" />
<ArrowKeybind keyboardKey="ArrowRight" />
{groups
{datePeriodGroups
.concat([[last6MonthsLinkItem]])
.flatMap((group) =>
group

View File

@ -398,7 +398,12 @@ export const getDatePeriodGroups = (
export const last6MonthsLinkItem: LinkItem = [
['Last 6 months', 'S'],
{
search: (s) => ({ ...s, period: QueryPeriod['6mo'], keybindHint: 'S' }),
search: (s) => ({
...s,
...clearedDateSearch,
period: QueryPeriod['6mo'],
keybindHint: 'S'
}),
isActive: ({ query }) => query.period === QueryPeriod['6mo']
}
]

View File

@ -4,6 +4,21 @@ import { formatISO, nowForSite, shiftMonths, yesterday } from './date'
jest.useFakeTimers()
describe(`${nowForSite.name} and ${formatISO.name}`, () => {
/* prettier-ignore */
const cases = [
[ 'Los Angeles/America', -3600 * 6, '2024-11-01T20:00:00.000Z', '2024-11-01' ],
[ 'Sydney/Australia', 3600 * 6, '2024-11-01T20:00:00.000Z', '2024-11-02' ]
]
test.each(cases)(
'in timezone of %s (offset %p) at %s, today is %s',
(_tz, offset, utcTime, expectedToday) => {
jest.setSystemTime(new Date(utcTime))
expect(formatISO(nowForSite({ offset }))).toEqual(expectedToday)
}
)
})
/* prettier-ignore */
const dstChangeOverDayEstonia = [
// system time today yesterday today-2mo today+2mo today-12mo offset

View File

@ -3,7 +3,7 @@
"version": "1.4.0",
"license": "AGPL-3.0-or-later",
"scripts": {
"test": "jest",
"test": "TZ=UTC jest",
"format": "prettier --write",
"check-format": "prettier --check **/*.{js,css,ts,tsx} --require-pragma",
"eslint": "eslint js/**",

View File

@ -1,13 +0,0 @@
/**
* @format
*/
/**
* @returns sets a fixed timezone for the test process,
* otherwise test runs on different servers and machines may be inconsistent
*/
function setFixedTimezone() {
process.env.TZ = 'UTC'
}
setFixedTimezone()