From 70179e94d50c412690b8db39456b3429ca52bb63 Mon Sep 17 00:00:00 2001 From: balajis-qb Date: Sun, 6 Apr 2025 23:24:32 +0530 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8E=A8=F0=9F=94=A8=20Re-adjust=20the?= =?UTF-8?q?=20height=20of=20TimePicker=20on=20Calendar=20resize=20due=20to?= =?UTF-8?q?=20number=20of=20days?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #5466 --- src/test/test_utils.ts | 20 ++++++++++++++ src/test/timepicker_test.test.tsx | 46 +++++++++++++++++++++++++++++++ src/time.tsx | 32 +++++++++++++++++---- 3 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/test/test_utils.ts b/src/test/test_utils.ts index 54f8073c57..4c33495b6d 100644 --- a/src/test/test_utils.ts +++ b/src/test/test_utils.ts @@ -123,6 +123,26 @@ export const safeQuerySelectorAll = ( return elements; }; +export const setupMockResizeObserver = () => { + const mockObserve = jest.fn(); + const mockDisconnect = jest.fn(); + const mockUnobserve = jest.fn(); + + const ResizeObserverMock = jest.fn(() => ({ + observe: mockObserve, + disconnect: mockDisconnect, + unobserve: jest.fn(), + })); + + global.ResizeObserver = ResizeObserverMock; + + return { + observe: mockObserve, + disconnect: mockDisconnect, + unobserve: mockUnobserve, + }; +}; + export class SafeElementWrapper { constructor(private element: T) {} diff --git a/src/test/timepicker_test.test.tsx b/src/test/timepicker_test.test.tsx index 3ed6d043e5..4c653d2c1c 100644 --- a/src/test/timepicker_test.test.tsx +++ b/src/test/timepicker_test.test.tsx @@ -9,6 +9,7 @@ import { SafeElementWrapper, safeQuerySelector, safeQuerySelectorAll, + setupMockResizeObserver, } from "./test_utils"; const MIN_TIME_LI_LEN = 2; @@ -19,10 +20,55 @@ describe("TimePicker", () => { let onChangeMoment: Date | undefined; let instance: DatePicker | null = null; + let mockObserve: jest.Mock, mockDisconnect: jest.Mock; + + beforeAll(() => { + const { observe, disconnect } = setupMockResizeObserver(); + mockObserve = observe; + mockDisconnect = disconnect; + }); + beforeEach(() => { div = document.createElement("div"); }); + describe("Re-adjust height on Calendar Height Change", () => { + beforeEach(() => { + mockObserve.mockReset(); + mockDisconnect.mockReset(); + }); + + it("calls observe on mount", async () => { + render( + , + ); + await waitFor(() => { + expect(mockObserve).toHaveBeenCalledTimes(1); + }); + }); + + it("calls disconnect on unmount", async () => { + const component = render( + , + ); + + component.unmount(); + await waitFor(() => { + expect(mockDisconnect).toHaveBeenCalledTimes(1); + }); + }); + }); + it("should update on input time change", () => { renderDatePicker("February 28, 2018 4:43 PM"); expect(getInputString()).toBe("February 28, 2018 4:43 PM"); diff --git a/src/time.tsx b/src/time.tsx index 4e1bb3d513..c6ed61e4b1 100644 --- a/src/time.tsx +++ b/src/time.tsx @@ -62,6 +62,7 @@ export default class Time extends Component { ); }; + private resizeObserver?: ResizeObserver; state: TimeState = { height: null, }; @@ -69,11 +70,11 @@ export default class Time extends Component { componentDidMount(): void { // code to ensure selected time will always be in focus within time window when it first appears this.scrollToTheSelectedTime(); - if (this.props.monthRef && this.header) { - this.setState({ - height: this.props.monthRef.clientHeight - this.header.clientHeight, - }); - } + this.observeDatePickerHeightChanges(); + } + + componentWillUnmount(): void { + this.resizeObserver?.disconnect(); } private header?: HTMLDivElement; @@ -82,6 +83,27 @@ export default class Time extends Component { private centerLi?: HTMLLIElement; + private observeDatePickerHeightChanges(): void { + const { monthRef } = this.props; + this.updateContainerHeight(); + + if (monthRef) { + this.resizeObserver = new ResizeObserver(() => { + this.updateContainerHeight(); + }); + + this.resizeObserver.observe(monthRef); + } + } + + private updateContainerHeight(): void { + if (this.props.monthRef && this.header) { + this.setState({ + height: this.props.monthRef.clientHeight - this.header.clientHeight, + }); + } + } + scrollToTheSelectedTime = (): void => { requestAnimationFrame((): void => { if (!this.list) return; From 7ced7074cd745157d8fd8951199bd6a042744a21 Mon Sep 17 00:00:00 2001 From: balajis-qb Date: Sun, 6 Apr 2025 23:26:10 +0530 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A7=AA=E2=99=BB=EF=B8=8F=20Setup=20mo?= =?UTF-8?q?ck=20ResizeObserver=20to=20make=20the=20test=20suites=20that=20?= =?UTF-8?q?use=20TimePicker=20pass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Related Commit: 70179e94 Related issue: #5466 --- src/test/calendar_test.test.tsx | 5 +++++ src/test/datepicker_test.test.tsx | 10 +++++++++- src/test/exclude_time_period_test.test.tsx | 5 +++++ src/test/exclude_times_test.test.tsx | 5 +++++ src/test/min_time_test.test.tsx | 6 +++++- src/test/show_time_test.test.tsx | 6 +++++- src/test/time_input_test.test.tsx | 6 +++++- 7 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/test/calendar_test.test.tsx b/src/test/calendar_test.test.tsx index 81ac627b5d..8d2814668f 100644 --- a/src/test/calendar_test.test.tsx +++ b/src/test/calendar_test.test.tsx @@ -45,6 +45,7 @@ import { SafeElementWrapper, safeQuerySelector, safeQuerySelectorAll, + setupMockResizeObserver, } from "./test_utils"; import type { ReactDatePickerCustomHeaderProps } from "../calendar"; @@ -136,6 +137,10 @@ describe("Calendar", () => { }; } + beforeAll(() => { + setupMockResizeObserver(); + }); + it("should start with the current date in view if no date range", () => { const now = newDate(); const { instance } = getCalendar(); diff --git a/src/test/datepicker_test.test.tsx b/src/test/datepicker_test.test.tsx index eeb3d83976..21548de05e 100644 --- a/src/test/datepicker_test.test.tsx +++ b/src/test/datepicker_test.test.tsx @@ -29,7 +29,11 @@ import DatePicker, { registerLocale } from "../index"; import CustomInput from "./helper_components/custom_input"; import ShadowRoot from "./helper_components/shadow_root"; import TestWrapper from "./helper_components/test_wrapper"; -import { getKey, safeQuerySelector } from "./test_utils"; +import { + getKey, + safeQuerySelector, + setupMockResizeObserver, +} from "./test_utils"; function getSelectedDayNode(container: HTMLElement) { return ( @@ -83,6 +87,10 @@ const showDocument = (calendarInput: HTMLElement) => { }; describe("DatePicker", () => { + beforeEach(() => { + setupMockResizeObserver(); + }); + afterEach(() => { jest.resetAllMocks(); }); diff --git a/src/test/exclude_time_period_test.test.tsx b/src/test/exclude_time_period_test.test.tsx index 6531bf649f..68bc3acc0f 100644 --- a/src/test/exclude_time_period_test.test.tsx +++ b/src/test/exclude_time_period_test.test.tsx @@ -2,9 +2,14 @@ import { render } from "@testing-library/react"; import React from "react"; import { newDate, setTime } from "../date_utils"; +import { setupMockResizeObserver } from "./test_utils"; import DatePicker from "../index"; describe("DatePicker", () => { + beforeAll(() => { + setupMockResizeObserver(); + }); + it("should only display times between minTime and maxTime", () => { const now = newDate(); const { container } = render( diff --git a/src/test/exclude_times_test.test.tsx b/src/test/exclude_times_test.test.tsx index 69745a3e3a..d477d80893 100644 --- a/src/test/exclude_times_test.test.tsx +++ b/src/test/exclude_times_test.test.tsx @@ -2,11 +2,16 @@ import { render } from "@testing-library/react"; import React from "react"; import { setTime, newDate } from "../date_utils"; +import { setupMockResizeObserver } from "./test_utils"; import DatePicker from "../index"; describe("DatePicker", () => { let now: Date, excludeTimes: Date[]; + beforeAll(() => { + setupMockResizeObserver(); + }); + beforeEach(() => { now = newDate(); excludeTimes = [ diff --git a/src/test/min_time_test.test.tsx b/src/test/min_time_test.test.tsx index 2eaf280c74..d705695294 100644 --- a/src/test/min_time_test.test.tsx +++ b/src/test/min_time_test.test.tsx @@ -3,7 +3,7 @@ import React, { useState } from "react"; import DatePicker from "../index"; -import { safeQuerySelector } from "./test_utils"; +import { safeQuerySelector, setupMockResizeObserver } from "./test_utils"; import type { DatePickerProps } from "../index"; @@ -44,6 +44,10 @@ const DatePickerWithState = ( }; describe("Datepicker minTime", () => { + beforeAll(() => { + setupMockResizeObserver(); + }); + it("should select time 12:00 AM when no minTime constraint is set.", () => { const { getByText, container } = render(); diff --git a/src/test/show_time_test.test.tsx b/src/test/show_time_test.test.tsx index 0fdfbf442d..a64a4785e6 100644 --- a/src/test/show_time_test.test.tsx +++ b/src/test/show_time_test.test.tsx @@ -4,9 +4,13 @@ import React from "react"; import DatePicker from "../index"; import TimeComponent from "../time"; -import { safeQuerySelector } from "./test_utils"; +import { safeQuerySelector, setupMockResizeObserver } from "./test_utils"; describe("DatePicker", () => { + beforeAll(() => { + setupMockResizeObserver(); + }); + it("should show time component when showTimeSelect prop is present", () => { const { container } = render(); const timeComponent = container.querySelector( diff --git a/src/test/time_input_test.test.tsx b/src/test/time_input_test.test.tsx index bc332753ec..ef4bf1d7bc 100644 --- a/src/test/time_input_test.test.tsx +++ b/src/test/time_input_test.test.tsx @@ -5,9 +5,13 @@ import DatePicker from "../index"; import InputTimeComponent from "../input_time"; import CustomTimeInput from "./helper_components/custom_time_input"; -import { safeQuerySelector } from "./test_utils"; +import { safeQuerySelector, setupMockResizeObserver } from "./test_utils"; describe("timeInput", () => { + beforeEach(() => { + setupMockResizeObserver(); + }); + afterEach(() => { jest.resetAllMocks(); }); From c12270ee82c712e7e211c918bf81c41ebcd8b944 Mon Sep 17 00:00:00 2001 From: balajis-qb Date: Mon, 7 Apr 2025 21:42:26 +0530 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A7=AA=20Add=20a=20shared=20utility?= =?UTF-8?q?=20to=20cache=20and=20test=20the=20received=20ResizeObserverCal?= =?UTF-8?q?lback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #5466 --- src/test/test_utils.ts | 21 ++++++++++++++++----- src/test/timepicker_test.test.tsx | 11 +++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/test/test_utils.ts b/src/test/test_utils.ts index 4c33495b6d..83e075a00d 100644 --- a/src/test/test_utils.ts +++ b/src/test/test_utils.ts @@ -123,16 +123,27 @@ export const safeQuerySelectorAll = ( return elements; }; +let _resizeObserverCallbackFn: ResizeObserverCallback | null; + +export const getResizeObserverCallback = () => _resizeObserverCallbackFn; + export const setupMockResizeObserver = () => { const mockObserve = jest.fn(); const mockDisconnect = jest.fn(); const mockUnobserve = jest.fn(); - const ResizeObserverMock = jest.fn(() => ({ - observe: mockObserve, - disconnect: mockDisconnect, - unobserve: jest.fn(), - })); + const ResizeObserverMock = jest.fn((fn) => { + _resizeObserverCallbackFn = fn; + + return { + observe: mockObserve, + disconnect: () => { + _resizeObserverCallbackFn = null; + mockDisconnect(); + }, + unobserve: jest.fn(), + }; + }); global.ResizeObserver = ResizeObserverMock; diff --git a/src/test/timepicker_test.test.tsx b/src/test/timepicker_test.test.tsx index 4c653d2c1c..b4c8010f44 100644 --- a/src/test/timepicker_test.test.tsx +++ b/src/test/timepicker_test.test.tsx @@ -6,6 +6,7 @@ import DatePicker from "../index"; import { getKey, + getResizeObserverCallback, SafeElementWrapper, safeQuerySelector, safeQuerySelectorAll, @@ -49,6 +50,14 @@ describe("TimePicker", () => { ); await waitFor(() => { expect(mockObserve).toHaveBeenCalledTimes(1); + + const resizeObserverCallback = getResizeObserverCallback(); + const mockObserveElement = mockObserve.mock.calls[0][0]; + expect(typeof resizeObserverCallback).toBe("function"); + + if (resizeObserverCallback) { + resizeObserverCallback([], mockObserveElement); + } }); }); @@ -65,6 +74,8 @@ describe("TimePicker", () => { component.unmount(); await waitFor(() => { expect(mockDisconnect).toHaveBeenCalledTimes(1); + const resizeObserverCallback = getResizeObserverCallback(); + expect(resizeObserverCallback).toBe(null); }); }); });