UNPKG

flatpickr

Version:

A lightweight, powerful javascript datetime picker

1,268 lines (1,016 loc) 36.9 kB
import flatpickr from "index"; import { Russian } from "l10n/ru"; import { Instance, DayElement } from "types/instance"; import { Options, DateRangeLimit } from "types/options"; flatpickr.defaultConfig.animate = false; flatpickr.defaultConfig.closeOnSelect = true; jest.useFakeTimers(); let elem: undefined | HTMLInputElement, fp: Instance; const UA = navigator.userAgent; let mockAgent: string | undefined; (navigator as any).__defineGetter__("userAgent", function() { return mockAgent || UA; }); function createInstance(config?: Options, el?: HTMLElement) { fp = flatpickr( el || elem || document.createElement("input"), config || {} ) as Instance; return fp; } function beforeEachTest() { mockAgent = undefined; jest.runAllTimers(); (document.activeElement as HTMLElement).blur(); fp && fp.destroy && fp.destroy(); if (elem === undefined) { elem = document.createElement("input"); document.body.appendChild(elem); } } function incrementTime( type: | "currentYearElement" | "hourElement" | "minuteElement" | "secondElement", by: number ) { const e = fp[type]; const times = Math.abs(by); const childNodeNum = by >= 0 ? 1 : 2; if (e !== undefined && e.parentNode) for (let i = times; i--; ) simulate( "mousedown", e.parentNode.childNodes[childNodeNum], { which: 1 }, MouseEvent ); } function simulate( eventType: string, onElement: Node, options?: object, type?: any ) { const eventOptions = Object.assign(options || {}, { bubbles: true }); const evt = new (type || CustomEvent)(eventType, eventOptions); try { Object.assign(evt, eventOptions); } catch (e) {} onElement.dispatchEvent(evt); } describe("flatpickr", () => { beforeEach(beforeEachTest); describe("init", () => { it("should parse defaultDate", () => { createInstance({ defaultDate: "2016-12-27T16:16:22.585Z", enableTime: true, }); const date = new Date("2016-12-27T16:16:22.585Z"); expect(fp.currentYear).toEqual(date.getFullYear()); expect(fp.currentMonth).toEqual(date.getMonth()); const selected = fp.days.querySelector(".selected"); expect(selected).toBeTruthy(); if (selected) expect(selected.textContent).toEqual(date.getDate() + ""); }); it("shouldn't parse out-of-bounds defaultDate", () => { createInstance({ minDate: "2016-12-28T16:16:22.585Z", defaultDate: "2016-12-27T16:16:22.585Z", }); expect(fp.days.querySelector(".selected")).toEqual(null); createInstance({ defaultDate: "2016-12-27T16:16:22.585Z", enableTime: true, }); fp.set("maxDate", "2016-12-25"); fp.set("minDate", "2016-12-24"); expect(fp.currentMonth).toEqual(11); expect(fp.days.querySelector(".selected")).toEqual(null); let enabledDays = fp.days.querySelectorAll( ".flatpickr-day:not(.disabled)" ); expect(enabledDays.length).toEqual(2); expect(enabledDays[0].textContent).toEqual("24"); expect(enabledDays[1].textContent).toEqual("25"); createInstance({ defaultDate: "2016-12-27T16:16:22.585Z", minDate: "2016-12-27T16:26:22.585Z", enableTime: true, }); expect(fp.selectedDates.length).toBe(0); expect(fp.days.querySelector(".selected")).toEqual(null); }); it("doesn't throw with undefined properties", () => { createInstance({ onChange: undefined, }); fp.set("minDate", "2016-10-20"); expect(fp.config.minDate).toBeDefined(); }); }); describe("datetimestring parser", () => { describe("date string parser", () => { it("should parse timestamp", () => { createInstance({ defaultDate: 1477111633771, }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getFullYear()).toEqual(2016); expect(fp.selectedDates[0].getMonth()).toEqual(9); expect(fp.selectedDates[0].getDate()).toEqual(22); }); it("should parse unix time", () => { createInstance({ defaultDate: "1477111633.771", // shouldnt parse as a timestamp dateFormat: "U", }); const parsedDate = fp.selectedDates[0]; expect(parsedDate).toBeDefined(); expect(parsedDate.getFullYear()).toEqual(2016); expect(parsedDate.getMonth()).toEqual(9); expect(parsedDate.getDate()).toEqual(22); }); it('should parse "2016-10"', () => { createInstance({ defaultDate: "2016-10", }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getFullYear()).toEqual(2016); expect(fp.selectedDates[0].getMonth()).toEqual(9); }); it('should parse "2016-10-20 3:30"', () => { createInstance({ defaultDate: "2016-10-20 3:30", enableTime: true, }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getFullYear()).toEqual(2016); expect(fp.selectedDates[0].getMonth()).toEqual(9); expect(fp.selectedDates[0].getDate()).toEqual(20); expect(fp.selectedDates[0].getHours()).toEqual(3); expect(fp.selectedDates[0].getMinutes()).toEqual(30); }); it("should parse ISO8601", () => { createInstance({ defaultDate: "2007-03-04T21:08:12", enableTime: true, enableSeconds: true, }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getFullYear()).toEqual(2007); expect(fp.selectedDates[0].getMonth()).toEqual(2); expect(fp.selectedDates[0].getDate()).toEqual(4); expect(fp.selectedDates[0].getHours()).toEqual(21); expect(fp.selectedDates[0].getMinutes()).toEqual(8); expect(fp.selectedDates[0].getSeconds()).toEqual(12); }); it('should parse "today"', () => { createInstance({}); const today = fp.parseDate("today", undefined, true); expect(today).toBeDefined(); today && expect(today.getHours()).toBe(0); }); it("should parse AM/PM", () => { createInstance({ dateFormat: "m/d/Y h:i K", enableTime: true, defaultDate: "8/3/2017 12:00 AM", }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getFullYear()).toEqual(2017); expect(fp.selectedDates[0].getMonth()).toEqual(7); expect(fp.selectedDates[0].getDate()).toEqual(3); expect(fp.selectedDates[0].getHours()).toEqual(0); expect(fp.selectedDates[0].getMinutes()).toEqual(0); }); it("should parse JSON datestrings", () => { createInstance({}); const date = fp.parseDate("2016-12-27T16:16:22.585Z", undefined); expect(date).toBeDefined(); if (!date) return; expect(date.getTime()).toBeDefined(); expect(date.getTime()).toEqual(Date.parse("2016-12-27T16:16:22.585Z")); }); }); describe("time string parser", () => { it('should parse "21:11"', () => { createInstance({ defaultDate: "21:11", allowInput: true, enableTime: true, noCalendar: true, }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getHours()).toEqual(21); expect(fp.selectedDates[0].getMinutes()).toEqual(11); }); it('should parse "21:11:12"', () => { createInstance({ allowInput: true, enableTime: true, enableSeconds: true, noCalendar: true, defaultDate: "21:11:12", }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getHours()).toEqual(21); expect(fp.selectedDates[0].getMinutes()).toEqual(11); expect(fp.selectedDates[0].getSeconds()).toEqual(12); }); it('should parse "11:59 PM"', () => { createInstance({ allowInput: true, enableTime: true, noCalendar: true, dateFormat: "h:i K", defaultDate: "11:59 PM", }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getHours()).toBe(23); expect(fp.selectedDates[0].getMinutes()).toBe(59); expect(fp.selectedDates[0].getSeconds()).toBe(0); expect(fp.amPM).toBeDefined(); fp.amPM && expect(fp.amPM.innerHTML).toBe("PM"); }); it('should parse "3:05:03 PM"', () => { createInstance({ allowInput: true, enableTime: true, enableSeconds: true, noCalendar: true, dateFormat: "h:i:S K", defaultDate: "3:05:03 PM", }); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getHours()).toBe(15); expect(fp.selectedDates[0].getMinutes()).toBe(5); expect(fp.selectedDates[0].getSeconds()).toBe(3); expect(fp.amPM).toBeDefined(); fp.amPM && expect(fp.amPM.innerHTML).toBe("PM"); }); it("should parse defaultHour", () => { createInstance({ enableTime: true, noCalendar: true, defaultHour: 0, }); expect((fp.hourElement as HTMLInputElement).value).toEqual("12"); createInstance({ enableTime: true, noCalendar: true, defaultHour: 12, }); expect((fp.hourElement as HTMLInputElement).value).toEqual("12"); createInstance({ enableTime: true, noCalendar: true, defaultHour: 23, time_24hr: true, }); expect((fp.hourElement as HTMLInputElement).value).toEqual("23"); }); }); }); describe("date formatting", () => { describe("default formatter", () => { const DEFAULT_FORMAT_1 = "d.m.y H:i:S", DEFAULT_FORMAT_2 = "D j F, 'y"; it(`should format the date with the pattern "${DEFAULT_FORMAT_1}"`, () => { const RESULT = "20.10.16 09:19:59"; createInstance({ dateFormat: DEFAULT_FORMAT_1, }); fp.setDate("20.10.16 09:19:59"); expect(fp.input.value).toEqual(RESULT); fp.setDate("2015.11.21 19:29:49"); expect(fp.input.value).not.toEqual(RESULT); }); it(`should format the date with the pattern "${DEFAULT_FORMAT_2}"`, () => { const RESULT = "Thu 20 October, '16"; createInstance({ dateFormat: DEFAULT_FORMAT_2, }); fp.setDate("Thu 20 October, '16"); expect(fp.input.value).toEqual(RESULT); fp.setDate("2015-11-21 19:29:49"); expect(fp.input.value).not.toEqual(RESULT); }); }); describe("custom formatter", () => { it("should format the date using the custom formatter", () => { const RESULT = "MAAAGIC.*^*.2016.*^*.20.*^*.10"; createInstance({ dateFormat: "YEAR-DAYOFMONTH-MONTH", formatDate(date, formatStr) { let segs = formatStr.split("-"); return ( "MAAAGIC.*^*." + segs .map(seg => { let mapped = null; switch (seg) { case "DAYOFMONTH": mapped = date.getDate(); break; case "MONTH": mapped = date.getMonth() + 1; break; case "YEAR": mapped = date.getFullYear(); break; } return "" + mapped; }) .join(".*^*.") ); }, }); fp.setDate(new Date(2016, 9, 20)); expect(fp.input.value).toEqual(RESULT); fp.setDate(new Date(2016, 10, 20)); expect(fp.input.value).not.toEqual(RESULT); }); }); }); describe("API", () => { it("changeMonth()", () => { createInstance({ defaultDate: "2016-12-20", }); fp.changeMonth(1); expect(fp.currentYear).toEqual(2017); fp.changeMonth(-1); expect(fp.currentYear).toEqual(2016); fp.changeMonth(2); expect(fp.currentMonth).toEqual(1); expect(fp.currentYear).toEqual(2017); fp.changeMonth(14); expect(fp.currentYear).toEqual(2018); expect(fp.currentMonth).toEqual(3); }); it("monthScroll", () => { createInstance(); fp.changeMonth(1, false); fp.open(); simulate("wheel", fp.currentMonthElement, { wheelDelta: 1, }); jest.runAllTimers(); expect(fp.currentMonth).toEqual(2); }); it("monthScroll: 0 < abs(delta) < 1", () => { createInstance(); fp.changeMonth(1, false); fp.open(); simulate("wheel", fp.currentMonthElement, { deltaY: -0.3, }); jest.runAllTimers(); expect(fp.currentMonth).toEqual(2); }); it("yearScroll", () => { createInstance(); const now = new Date(); fp.setDate(now); fp.open(); simulate( "wheel", fp.currentYearElement, { wheelDelta: 1, }, MouseEvent ); jest.runAllTimers(); expect(fp.currentYear).toEqual(now.getFullYear() + 1); }); it("destroy()", () => { let fired = false; const input = fp.input; createInstance({ altInput: true, onKeyDown: [ () => { fired = true; }, ], }); expect(input.type).toEqual("hidden"); fp.open(); fp.altInput && simulate("keydown", fp.altInput, { key: "ArrowLeft", bubbles: true }); expect(fired).toEqual(true); fp.destroy(); expect(input.type).toEqual("text"); expect(fp.altInput).toBeUndefined(); expect(fp.config).toBeUndefined(); fired = false; simulate("keydown", input, { key: "ArrowLeft", bubbles: true }); simulate("keydown", document.body, { key: "ArrowLeft", bubbles: true }); expect(fired).toEqual(false); }); it("set (option, value)", () => { createInstance(); fp.set("minDate", "2016-10-20"); expect(fp.currentYearElement.min).toEqual("2016"); expect(fp.config.minDate).toBeDefined(); fp.set("minDate", null); expect(fp.currentYearElement.hasAttribute("min")).toEqual(false); fp.set("maxDate", "2016-10-20"); expect(fp.config.maxDate).toBeDefined(); expect(fp.currentYearElement.max).toEqual("2016"); fp.set("maxDate", null); expect(fp.currentYearElement.hasAttribute("max")).toEqual(false); fp.set("mode", "range"); expect(fp.config.mode).toEqual("range"); }); it("setDate (date)", () => { createInstance({ enableTime: true, }); fp.setDate("2016-10-20 03:00"); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getFullYear()).toEqual(2016); expect(fp.selectedDates[0].getMonth()).toEqual(9); expect(fp.selectedDates[0].getDate()).toEqual(20); expect(fp.selectedDates[0].getHours()).toEqual(3); expect(fp.currentYear).toEqual(2016); expect(fp.currentMonth).toEqual(9); if (fp.hourElement && fp.minuteElement && fp.amPM) { expect(fp.hourElement.value).toEqual("03"); expect(fp.minuteElement.value).toEqual("00"); expect(fp.amPM.textContent).toEqual("AM"); } fp.setDate(0); expect(fp.selectedDates[0]).toBeDefined(); expect(fp.selectedDates[0].getFullYear()).toBeLessThan(1971); fp.setDate(""); expect(fp.selectedDates[0]).not.toBeDefined(); }); it("has valid latestSelectedDateObj", () => { createInstance({ defaultDate: "2016-10-01 3:30", enableTime: true, }); expect(fp.latestSelectedDateObj).toBeDefined(); if (fp.latestSelectedDateObj) { expect(fp.latestSelectedDateObj.getFullYear()).toEqual(2016); expect(fp.latestSelectedDateObj.getMonth()).toEqual(9); expect(fp.latestSelectedDateObj.getDate()).toEqual(1); } if (fp.hourElement && fp.minuteElement && fp.amPM) { expect(fp.hourElement.value).toEqual("03"); expect(fp.minuteElement.value).toEqual("30"); expect(fp.amPM.textContent).toEqual("AM"); } fp.setDate("2016-11-03 16:49"); expect(fp.latestSelectedDateObj).toBeDefined(); if (fp.latestSelectedDateObj) { expect(fp.latestSelectedDateObj.getFullYear()).toEqual(2016); expect(fp.latestSelectedDateObj.getMonth()).toEqual(10); expect(fp.latestSelectedDateObj.getDate()).toEqual(3); } if (fp.hourElement && fp.minuteElement && fp.amPM) { expect(fp.hourElement.value).toEqual("04"); expect(fp.minuteElement.value).toEqual("49"); expect(fp.amPM.textContent).toEqual("PM"); } fp.setDate(""); expect(fp.latestSelectedDateObj).toEqual(undefined); }); it("parses dates in enable[] and disable[]", () => { createInstance({ disable: [ { from: "2016-11-20", to: "2016-12-20" }, "2016-12-21", null as any, ], enable: [ { from: "2016-11-20", to: "2016-12-20" }, "2016-12-21", null as any, ], }); expect( (fp.config.disable[0] as DateRangeLimit).from instanceof Date ).toBe(true); expect((fp.config.disable[0] as DateRangeLimit).to instanceof Date).toBe( true ); expect(fp.config.disable[1] instanceof Date).toBe(true); expect(fp.config.disable.indexOf(null as any)).toBe(-1); expect((fp.config.enable[0] as DateRangeLimit).from instanceof Date).toBe( true ); expect((fp.config.enable[0] as DateRangeLimit).to instanceof Date).toBe( true ); expect(fp.config.enable[1] instanceof Date).toBe(true); expect(fp.config.enable.indexOf(null as any)).toBe(-1); }); it("documentClick", () => { createInstance({ mode: "range", }); simulate("focus", fp._input, { which: 1, bubbles: true }, CustomEvent); fp._input.focus(); expect(fp.isOpen).toBe(true); simulate("mousedown", window.document.body, { which: 1 }, CustomEvent); fp._input.blur(); expect(fp.isOpen).toBe(false); expect(fp.calendarContainer.classList.contains("open")).toBe(false); expect(fp.selectedDates.length).toBe(0); simulate("focus", fp._input); simulate( "mousedown", fp.days.childNodes[15], { which: 1, bubbles: true }, CustomEvent ); expect(fp.selectedDates.length).toBe(1); fp.isOpen = true; simulate("mousedown", window.document.body, { which: 1 }, CustomEvent); expect(fp.isOpen).toBe(false); expect(fp.selectedDates.length).toBe(0); expect(fp._input.value).toBe(""); }); it("onKeyDown", () => { createInstance({ enableTime: true, altInput: true, }); fp.jumpToDate("2016-2-1"); fp.open(); (fp.days.childNodes[15] as HTMLSpanElement).focus(); simulate( "keydown", fp.days.childNodes[15], { key: "Enter", }, KeyboardEvent ); expect(fp.selectedDates.length).toBe(1); simulate( "keydown", fp.calendarContainer, { key: "Escape", }, KeyboardEvent ); expect(fp.isOpen).toEqual(false); }); it("onKeyDown: arrow nav", () => { jest.runAllTimers(); createInstance({ defaultDate: "2017-01-01", }); fp.open(); fp.input.focus(); simulate( "keydown", window.document.body, { key: "ArrowLeft", bubbles: true }, KeyboardEvent ); let dayElem = document.activeElement as DayElement; expect(fp.currentMonth).toBe(0); expect(dayElem.dateObj.getDate()).toEqual(1); simulate("keydown", document.activeElement, { key: "ArrowLeft" }); expect(fp.currentMonth).toBe(11); expect(fp.currentYear).toBe(2016); dayElem = document.activeElement as DayElement; expect(dayElem.dateObj.getDate()).toEqual(7); simulate("keydown", document.activeElement, { key: "ArrowRight" }); dayElem = document.activeElement as DayElement; expect(dayElem.dateObj.getDate()).toEqual(1); expect(fp.currentMonth).toBe(0); expect(fp.currentYear).toBe(2017); simulate("keydown", document.activeElement, { key: "ArrowUp" }); simulate("keydown", document.activeElement, { key: "ArrowUp" }); expect(fp.currentMonth).toBe(11); expect(fp.currentYear).toBe(2016); dayElem = document.activeElement as DayElement; expect(dayElem.dateObj.getDate()).toEqual(25); simulate("keydown", document.activeElement, { key: "ArrowDown" }); simulate("keydown", document.activeElement, { key: "ArrowDown" }); expect(fp.currentMonth).toBe(0); expect(fp.currentYear).toBe(2017); dayElem = document.activeElement as DayElement; expect(dayElem.dateObj.getDate()).toEqual(1); simulate("keydown", document.activeElement, { key: "ArrowRight", ctrlKey: true, }); expect(fp.currentMonth).toBe(1); expect(fp.currentYear).toBe(2017); simulate("keydown", document.activeElement, { key: "ArrowLeft", ctrlKey: true, }); simulate("keydown", document.activeElement, { key: "ArrowLeft", ctrlKey: true, }); expect(fp.currentMonth).toBe(11); expect(fp.currentYear).toBe(2016); }); it("enabling dates by function", () => { createInstance({ enable: [d => d.getDate() === 6, new Date()], disable: [{ from: "2016-10-20", to: "2016-10-25" }], }); expect(fp.isEnabled("2016-10-06")).toBe(true); expect(fp.isEnabled(new Date())).toBe(true); expect(fp.isEnabled("2016-10-20")).toBe(false); expect(fp.isEnabled("2016-10-22")).toBe(false); expect(fp.isEnabled("2016-10-25")).toBe(false); }); }); describe("UI", () => { it("mode: multiple", () => { createInstance({ mode: "multiple", }); fp.jumpToDate("2017-1-1"); fp.open(); simulate("keydown", fp.days.childNodes[0], { key: "Enter" }); expect(fp.selectedDates.length).toBe(1); simulate("keydown", fp.days.childNodes[0], { key: "Enter" }); expect(fp.selectedDates.length).toBe(0); }); it("switch month to selectedDate", () => { createInstance(); fp.jumpToDate("2017-1-1"); expect(fp.currentMonth).toBe(0); simulate("mousedown", fp.days.childNodes[41], { which: 1 }, MouseEvent); expect(fp.selectedDates.length).toBe(1); expect(fp.currentMonth).toBe(1); }); it("static calendar", () => { createInstance({ static: true, }); expect(fp.calendarContainer.classList.contains("static")).toBe(true); if (!fp.element.parentNode) return; expect( (fp.element.parentNode as Element).classList.contains( "flatpickr-wrapper" ) ).toBe(true); expect(fp.element.parentNode.childNodes[0]).toEqual(fp.element); expect(fp.element.parentNode.childNodes[1]).toEqual(fp.calendarContainer); }); it("mobile calendar", () => { mockAgent = "Android"; createInstance({ enableTime: true, }); expect(fp.isMobile).toBe(true); const mobileInput = fp.mobileInput as HTMLInputElement; mobileInput.value = "2016-10-20T02:30"; simulate("change", mobileInput); expect(fp.selectedDates.length).toBe(1); expect(fp.latestSelectedDateObj).toBeDefined(); if (!fp.latestSelectedDateObj) return; expect(fp.latestSelectedDateObj.getFullYear()).toBe(2016); expect(fp.latestSelectedDateObj.getMonth()).toBe(9); expect(fp.latestSelectedDateObj.getDate()).toBe(20); expect(fp.latestSelectedDateObj.getHours()).toBe(2); expect(fp.latestSelectedDateObj.getMinutes()).toBe(30); }); it("selectDate() + onChange() through GUI", () => { function verifySelected(date: Date | undefined) { expect(date).toBeDefined(); if (!date) return; expect(date.getFullYear()).toEqual(2016); expect(date.getMonth()).toEqual(9); expect(date.getDate()).toEqual(10); expect((fp.hourElement as HTMLInputElement).value).toEqual("03"); expect((fp.minuteElement as HTMLInputElement).value).toEqual("30"); expect((fp.amPM as HTMLSpanElement).textContent).toEqual("AM"); } createInstance({ enableTime: true, defaultDate: "2016-10-01 3:30", onChange: dates => { if (dates.length) verifySelected(dates[0]); }, }); fp.open(); simulate("mousedown", fp.days.childNodes[15], { which: 1 }, MouseEvent); // oct 10 verifySelected(fp.selectedDates[0]); }); it("year input", () => { createInstance(); fp.currentYearElement.value = "2000"; simulate("keyup", fp.currentYearElement); expect(fp.currentYear).toEqual(2000); incrementTime("currentYearElement", 1); expect(fp.currentYear).toEqual(2001); expect(fp.currentYearElement.value).toEqual("2001"); expect( (fp.days.childNodes[10] as DayElement).dateObj.getFullYear() ).toEqual(2001); }); it("time input and increments", () => { createInstance({ enableTime: true, defaultDate: "2017-1-1 10:00", //minDate: "2017-1-01 3:35", }); expect(fp.hourElement).toBeDefined(); expect(fp.minuteElement).toBeDefined(); expect(fp.amPM).toBeDefined(); if (!fp.hourElement || !fp.minuteElement || !fp.amPM) return; expect(fp.hourElement.value).toEqual("10"); expect(fp.minuteElement.value).toEqual("00"); expect(fp.amPM.textContent).toEqual("AM"); incrementTime("hourElement", 1); expect(fp.hourElement.value).toEqual("11"); incrementTime("minuteElement", 1); expect(fp.minuteElement.value).toEqual("05"); simulate("mousedown", fp.amPM, { which: 1 }, MouseEvent); expect(fp.amPM.textContent).toEqual("PM"); simulate( "wheel", fp.hourElement, { wheelDelta: 1, }, MouseEvent ); expect(fp.hourElement.value).toEqual("12"); fp.hourElement.value = "9"; simulate("input", fp.hourElement); expect(fp.hourElement.value).toEqual("09"); }); it("time input respects minDate", () => { createInstance({ enableTime: true, dateFormat: "Y-m-d H:i", defaultDate: "2017-1-1 4:00", minDate: "2017-1-01 3:35", }); expect(!!fp.minDateHasTime).toBe(true); const [hourElem, minuteElem] = [ fp.hourElement as HTMLInputElement, fp.minuteElement as HTMLInputElement, ]; incrementTime("hourElement", -1); expect(hourElem.value).toEqual("03"); expect(minuteElem.value).toEqual("35"); incrementTime("hourElement", -1); expect(hourElem.value).toEqual("03"); // unchanged incrementTime("minuteElement", -1); expect(minuteElem.value).toEqual("35"); // can't go lower than min incrementTime("minuteElement", 1); expect(minuteElem.value).toEqual("40"); hourElem.value = "2"; simulate("input", hourElem); jest.runAllTimers(); expect(hourElem.value).toEqual("03"); minuteElem.value = "00"; simulate("input", minuteElem); expect(minuteElem.value).toEqual("35"); }); it("time input respects maxDate", () => { createInstance({ enableTime: true, defaultDate: "2017-1-1 3:00", maxDate: "2017-1-01 3:35", }); const [hourElem, minuteElem] = [ fp.hourElement as HTMLInputElement, fp.minuteElement as HTMLInputElement, ]; incrementTime("hourElement", -1); expect(hourElem.value).toEqual("02"); // ok incrementTime("hourElement", 3); expect(hourElem.value).toEqual("03"); incrementTime("minuteElement", 8); expect(minuteElem.value).toEqual("35"); // can't go higher than 35 }); it("time input respects same-day minDate/maxDate", () => { createInstance({ enableTime: true, minDate: "2017-1-01 2:00 PM", maxDate: "2017-1-01 3:35 PM", }); const [hourElem, minuteElem] = [ fp.hourElement as HTMLInputElement, fp.minuteElement as HTMLInputElement, ]; fp.setDate("2017-1-1 2:30 PM"); incrementTime("hourElement", -1); simulate( "wheel", hourElem, { wheelDelta: -1, }, MouseEvent ); expect(hourElem.value).toEqual("02"); // ok incrementTime("hourElement", 4); expect(hourElem.value).toEqual("03"); incrementTime("minuteElement", 8); simulate( "wheel", minuteElem, { wheelDelta: 1, }, MouseEvent ); expect(minuteElem.value).toEqual("35"); // can't go higher than 35 }); it("time picker: implicit selectedDate", () => { createInstance({ enableTime: true, noCalendar: true, }); expect(fp.selectedDates.length).toEqual(0); incrementTime("minuteElement", 1); expect(fp.selectedDates.length).toEqual(1); expect(fp.selectedDates[0].getDate()).toEqual(new Date().getDate()); }); it("time picker: minDate/maxDate + preloading", () => { createInstance({ enableTime: true, noCalendar: true, minDate: "02:30", defaultDate: "3:30", }); const [hours, minutes, amPM] = [ fp.hourElement as HTMLInputElement, fp.minuteElement as HTMLInputElement, fp.amPM as HTMLSpanElement, ]; expect(hours.value).toBe("03"); expect(minutes.value).toBe("30"); expect(amPM.textContent).toBe("AM"); incrementTime("hourElement", -1); expect(hours.value).toBe("02"); fp.set("maxDate", "04:30"); incrementTime("hourElement", 3); expect(hours.value).toBe("04"); simulate("mousedown", amPM, { which: 1 }, MouseEvent); expect(amPM.textContent).toBe("AM"); fp.clear(); fp.setDate("03:30"); expect(hours.value).toBe("03"); fp.setDate("05:30"); expect(hours.value).toBe("03"); fp.setDate("00:30"); expect(hours.value).toBe("03"); }); it("should delay time input validation on keydown", () => { createInstance({ enableTime: true, defaultDate: new Date().setHours(17, 30, 0, 0), minDate: new Date().setHours(16, 30, 0, 0), time_24hr: true, }); const hours = fp.hourElement as HTMLInputElement; hours.value = "16"; simulate("input", hours, {}, KeyboardEvent); jest.runAllTimers(); expect(hours.value).toEqual("16"); hours.value = "1"; simulate("input", hours); expect(hours.value).toEqual("1"); jest.runAllTimers(); jest.runAllTimers(); expect(hours.value).toEqual("16"); }); it("should have working strap mode", () => { let wrapper = document.createElement("div"); const input = document.createElement("input"); input.setAttribute("data-input", ""); wrapper.appendChild(input); ["open", "close", "toggle", "clear"].forEach(type => { let e = document.createElement("button"); e.setAttribute(`data-${type}`, ""); wrapper.appendChild(e); }); const instance = createInstance( { wrap: true, }, wrapper ); expect(instance.input).toEqual(input); simulate("click", wrapper.childNodes[1], { which: 1 }, MouseEvent); // open expect(instance.isOpen).toEqual(true); simulate("click", wrapper.childNodes[2], { which: 1 }, MouseEvent); // close expect(instance.isOpen).toEqual(false); simulate("click", wrapper.childNodes[3], { which: 1 }, MouseEvent); // toggle expect(instance.isOpen).toEqual(true); simulate("click", wrapper.childNodes[3], { which: 1 }, MouseEvent); expect(instance.isOpen).toEqual(false); instance.setDate(new Date()); expect(instance.selectedDates.length).toEqual(1); expect(instance.selectedDateElem).toBeDefined(); instance.selectedDateElem && expect( parseInt(instance.selectedDateElem.textContent as string) ).toEqual(new Date().getDate()); simulate("click", wrapper.childNodes[4], { which: 1 }, MouseEvent); // clear expect(instance.selectedDates.length).toEqual(0); expect(instance.input.value).toEqual(""); instance.destroy(); wrapper.parentNode && wrapper.parentNode.removeChild(wrapper); }); it("valid mouseover behavior in range mode", () => { createInstance({ mode: "range", }); const day = (i: number) => fp.days.childNodes[i] as DayElement; simulate("mouseover", fp.days.childNodes[15]); expect(fp.selectedDates.length).toEqual(0); fp.setDate("2016-1-17"); expect(fp.selectedDates.length).toEqual(1); simulate("mouseover", fp.days.childNodes[32]); expect(day(21).classList.contains("startRange")).toEqual(true); expect(day(32).classList.contains("endRange")).toEqual(true); for (let i = 22; i < 32; i++) expect(day(i).classList.contains("inRange")).toEqual(true); fp.clear(); fp.set("disable", ["2016-1-12", "2016-1-20"]); fp.setDate("2016-1-17"); simulate("mouseover", day(32)); expect(day(32).classList.contains("endRange")).toEqual(false); expect(day(24).classList.contains("disabled")).toEqual(true); expect(day(25).classList.contains("notAllowed")).toEqual(true); for (let i = 25; i < 32; i++) expect(day(i).classList.contains("inRange")).toEqual(false); for (let i = 17; i < 22; i++) { expect(day(i).classList.contains("notAllowed")).toEqual(false); expect(day(i).classList.contains("disabled")).toEqual(false); } simulate("mousedown", fp.days.childNodes[17], { which: 1 }, MouseEvent); expect(fp.selectedDates.length).toEqual(2); expect(fp.input.value).toEqual("2016-01-13 to 2016-01-17"); }); it("show and hide prev/next month arrows", () => { const isArrowVisible = (which: "prevMonthNav" | "nextMonthNav") => fp[which].style.display !== "none"; createInstance({ minDate: "2099-1-1", maxDate: "2099-3-4", mode: "range", }); expect(fp.currentMonth).toBe(0); expect(isArrowVisible("prevMonthNav")).toBe(false); expect(isArrowVisible("nextMonthNav")).toBe(true); simulate("mousedown", fp.days.childNodes[10], { which: 1 }, MouseEvent); // select some date jest.runOnlyPendingTimers(); simulate("mousedown", fp.nextMonthNav, { which: 1 }, MouseEvent); expect(isArrowVisible("prevMonthNav")).toBe(true); expect(isArrowVisible("nextMonthNav")).toBe(true); simulate("mousedown", fp.nextMonthNav, { which: 1 }, MouseEvent); expect(isArrowVisible("prevMonthNav")).toBe(true); expect(isArrowVisible("nextMonthNav")).toBe(false); }); }); describe("Localization", () => { it("By locale config option", () => { createInstance({ locale: Russian, }); expect(fp.l10n.months.longhand[0]).toEqual("Январь"); createInstance(); expect(fp.l10n.months.longhand[0]).toEqual("January"); }); it("By overriding default locale", () => { flatpickr.localize(Russian); expect(flatpickr.l10ns.default.months.longhand[0]).toEqual("Январь"); createInstance(); expect(fp.l10n.months.longhand[0]).toEqual("Январь"); }); it("correctly formats altInput", () => { createInstance({ locale: Russian, altInput: true, altFormat: "F", defaultDate: "2016-12-27T16:16:22.585Z", }); const altInput = fp.altInput as HTMLInputElement; expect(altInput.value).toEqual("Декабрь"); fp.destroy(); createInstance({ locale: "en", altInput: true, altFormat: "F", defaultDate: "2016-12-27T16:16:22.585Z", }); expect((fp.altInput as HTMLInputElement).value).toEqual("December"); }); }); });