Skip to content

Commit

Permalink
fix: (POC) Fixes calendar jumping when chaning month
Browse files Browse the repository at this point in the history
  • Loading branch information
pan-kot committed Jan 9, 2025
1 parent 1f1d269 commit 3919c00
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 31 deletions.
50 changes: 29 additions & 21 deletions src/calendar/__tests__/calendar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

import * as React from 'react';
import { fireEvent, render } from '@testing-library/react';
import addMonths from 'date-fns/addMonths';
import range from 'lodash/range';
import MockDate from 'mockdate';

import '../../__a11y__/to-validate-a11y';
Expand Down Expand Up @@ -42,13 +44,19 @@ function getDayText(wrapper: CalendarWrapper, row: number, col: number) {
return wrapper.findDateAt(row, col).findByClassName(styles['date-inner'])!.getElement().textContent;
}

describe('Calendar', () => {
test('check a11y', async () => {
const { container } = renderCalendar();
await expect(container).toValidateA11y();
});
test('check a11y', async () => {
const { container } = renderCalendar();
await expect(container).toValidateA11y();
});

test.each(range(0, 11).map(month => addMonths(new Date('2025-01-01'), month).toISOString().split('T')[0]))(
'always renders 42 days, value=%s',
value => {
renderCalendar({ value });
expect(document.querySelectorAll(`.${styles['calendar-date']}`)).toHaveLength(42);
}
);

describe('Calendar locale US', () => {
beforeEach(() => {
const locale = new Intl.DateTimeFormat('en-US', { timeZone: 'EST' });
Expand All @@ -60,6 +68,22 @@ describe('Calendar locale US', () => {
const { wrapper } = renderCalendar();
expect(findCalendarWeekdays(wrapper)[0]).toBe('Sun');
});

describe('Calendar header', () => {
test('previous button navigates to previous month', () => {
const { wrapper } = renderCalendar({ value: '2022-01-07' });
expect(wrapper.findHeader().getElement()).toHaveTextContent('January 2022');
wrapper.findPreviousButton().click();
expect(wrapper.findHeader().getElement()).toHaveTextContent('December 2021');
});

test('next button navigates to next month', () => {
const { wrapper } = renderCalendar({ value: '2022-01-07' });
expect(wrapper.findHeader().getElement()).toHaveTextContent('January 2022');
wrapper.findNextButton().click();
expect(wrapper.findHeader().getElement()).toHaveTextContent('February 2022');
});
});
});

describe('Calendar locale DE', () => {
Expand All @@ -75,22 +99,6 @@ describe('Calendar locale DE', () => {
});
});

describe('Calendar header', () => {
test('previous button navigates to previous month', () => {
const { wrapper } = renderCalendar({ value: '2022-01-07' });
expect(wrapper.findHeader().getElement()).toHaveTextContent('January 2022');
wrapper.findPreviousButton().click();
expect(wrapper.findHeader().getElement()).toHaveTextContent('December 2021');
});

test('next button navigates to next month', () => {
const { wrapper } = renderCalendar({ value: '2022-01-07' });
expect(wrapper.findHeader().getElement()).toHaveTextContent('January 2022');
wrapper.findNextButton().click();
expect(wrapper.findHeader().getElement()).toHaveTextContent('February 2022');
});
});

describe('aria labels', () => {
describe('aria-label', () => {
test('can be set', () => {
Expand Down
14 changes: 12 additions & 2 deletions src/calendar/grid/use-calendar-grid-rows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// SPDX-License-Identifier: Apache-2.0

import { useMemo } from 'react';
import { addMonths } from 'date-fns';
import { getCalendarMonth } from 'mnth';

import { normalizeStartOfWeek } from '../../internal/utils/locale/index.js';
import { DayIndex, normalizeStartOfWeek } from '../../internal/utils/locale/index.js';
import { CalendarProps } from '../interfaces.js';

export default function useCalendarGridRows({
Expand All @@ -24,7 +25,7 @@ export default function useCalendarGridRows({
() =>
isMonthPicker
? getCalendarYear(baseDate)
: getCalendarMonth(baseDate, { firstDayOfWeek: normalizeStartOfWeek(startOfWeek, locale) }),
: getCalendarMonthWithSixRows(baseDate, normalizeStartOfWeek(startOfWeek, locale)),
[baseDate, isMonthPicker, startOfWeek, locale]
);

Expand All @@ -38,3 +39,12 @@ function getCalendarYear(date: Date): Date[][] {
.fill(0)
.map((_, i: number) => new Array(3).fill(0).map((_, j: number) => new Date(year, i * 3 + j)));
}

function getCalendarMonthWithSixRows(date: Date, firstDayOfWeek: DayIndex) {
const rows = getCalendarMonth(date, { firstDayOfWeek });
if (rows.length === 6) {
return rows;
}
const nextRow = getCalendarMonth(addMonths(date, 1), { firstDayOfWeek })[1];
return [...rows, nextRow];
}
17 changes: 11 additions & 6 deletions src/date-picker/__tests__/date-picker-calendar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
import calendarStyles from '../../../lib/components/calendar/styles.selectors.js';
import screenreaderOnlyStyles from '../../../lib/components/internal/components/screenreader-only/styles.selectors.js';

const toLocaleDateString = window.Date.prototype.toLocaleDateString;

describe('Date picker calendar', () => {
const defaultProps: DatePickerProps = {
i18nStrings: {
Expand All @@ -40,7 +42,10 @@ describe('Date picker calendar', () => {
const locale = new Intl.DateTimeFormat('en-US', { timeZone: 'UTC' });
jest.spyOn(Intl, 'DateTimeFormat').mockImplementation(() => locale);
});
afterEach(() => jest.restoreAllMocks());
afterEach(() => {
jest.restoreAllMocks();
window.Date.prototype.toLocaleDateString = toLocaleDateString;
});

describe('basic calendar interaction', () => {
let wrapper: DatePickerWrapper, getByTestId: (selector: string) => HTMLElement;
Expand Down Expand Up @@ -117,17 +122,17 @@ describe('Date picker calendar', () => {
test('should allow locale override', () => {
const locale = 'de-DE';
const localStringMock = jest.fn().mockReturnValue('März 2018');
const oldImpl = window.Date.prototype.toLocaleDateString;
window.Date.prototype.toLocaleDateString = localStringMock;

const { wrapper } = renderDatePicker({ ...defaultProps, locale });
wrapper.findOpenCalendarButton().click();
expect(findCalendarHeaderText(wrapper)).toBe('März 2018');
// we render 2018/03/22 which results in
// -> 35 (5 weeks á 7 days) + 7 (weekday names) * 2 + 1 (month name)
expect(localStringMock).toHaveBeenCalledTimes(51);
// For each calendar we render 6 weeks (42 days) and each requires a label.
// Additionally, we generate short and full labels for weekday names (14 in total),
// and 2 labels for month name.
// 42 + 14 + 2 = 58.
expect(localStringMock).toHaveBeenCalledTimes(58);
expect(localStringMock).toHaveBeenCalledWith(locale, expect.any(Object));
window.Date.prototype.toLocaleDateString = oldImpl;
});

test('should override start day of week', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/internal/utils/locale/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

export { mergeLocales } from './merge-locales';
export { normalizeLocale } from './normalize-locale';
export { normalizeStartOfWeek } from './normalize-start-of-week';
export { normalizeStartOfWeek, DayIndex } from './normalize-start-of-week';
2 changes: 1 addition & 1 deletion src/internal/utils/locale/normalize-start-of-week.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { getWeekStartByLocale } from 'weekstart';

type DayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6;
export type DayIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6;

export function normalizeStartOfWeek(startOfWeek: number | undefined, locale: string) {
return (typeof startOfWeek === 'number' ? startOfWeek % 7 : getWeekStartByLocale(locale)) as DayIndex;
Expand Down

0 comments on commit 3919c00

Please sign in to comment.