@@ -1035,8 +1035,10 @@ describe("flatpickr", () => {
10351035 expect ( document . activeElement ) . toBe ( fp . nextMonthNav ) ;
10361036 } ) ;
10371037
1038- it ( "tab from day moves focus forward to close button" , ( ) => {
1039- createInstance ( ) ;
1038+ it ( "tab from day moves focus forward to time trigger" , ( ) => {
1039+ createInstance ( {
1040+ enableTime : true ,
1041+ } ) ;
10401042 fp . open ( ) ;
10411043
10421044 const firstDay = fp . days . childNodes [ 0 ] as HTMLElement ;
@@ -1046,7 +1048,10 @@ describe("flatpickr", () => {
10461048 keyCode : 9 ,
10471049 } ) ;
10481050
1049- expect ( document . activeElement ) . toBe ( fp . closeButton ) ;
1051+ const trigger = fp . timeContainer ?. querySelector (
1052+ ".flatpickr-time-wheel-trigger"
1053+ ) ;
1054+ expect ( document . activeElement ) . toBe ( trigger ) ;
10501055 } ) ;
10511056
10521057 it ( "pressing M focuses month control as keyboard fallback" , ( ) => {
@@ -1357,11 +1362,11 @@ describe("flatpickr", () => {
13571362 it ( "with value" , ( ) => {
13581363 mockAgent = "Android" ;
13591364 createInstance ( {
1360- enableTime : true ,
1365+ defaultDate : "2016-10-20" ,
13611366 } ) ;
13621367
13631368 const mobileInput = fp . mobileInput as HTMLInputElement ;
1364- mobileInput . value = "2016-10-20T02:30 " ;
1369+ mobileInput . value = "2016-10-20 " ;
13651370 simulate ( "change" , mobileInput ) ;
13661371
13671372 expect ( fp . selectedDates . length ) . toBe ( 1 ) ;
@@ -1371,8 +1376,18 @@ describe("flatpickr", () => {
13711376 expect ( fp . latestSelectedDateObj . getFullYear ( ) ) . toBe ( 2016 ) ;
13721377 expect ( fp . latestSelectedDateObj . getMonth ( ) ) . toBe ( 9 ) ;
13731378 expect ( fp . latestSelectedDateObj . getDate ( ) ) . toBe ( 20 ) ;
1374- expect ( fp . latestSelectedDateObj . getHours ( ) ) . toBe ( 2 ) ;
1375- expect ( fp . latestSelectedDateObj . getMinutes ( ) ) . toBe ( 30 ) ;
1379+ expect ( fp . latestSelectedDateObj . getHours ( ) ) . toBe ( 0 ) ;
1380+ expect ( fp . latestSelectedDateObj . getMinutes ( ) ) . toBe ( 0 ) ;
1381+ } ) ;
1382+
1383+ it ( "enableTime uses custom picker on mobile" , ( ) => {
1384+ mockAgent = "Android" ;
1385+ createInstance ( {
1386+ enableTime : true ,
1387+ } ) ;
1388+
1389+ expect ( fp . isMobile ) . toBe ( false ) ;
1390+ expect ( fp . mobileInput ) . toBeUndefined ( ) ;
13761391 } ) ;
13771392
13781393 it ( "copy className and adds own" , ( ) => {
@@ -1452,21 +1467,15 @@ describe("flatpickr", () => {
14521467 expect ( clickSpy ) . toHaveBeenCalled ( ) ;
14531468 } ) ;
14541469
1455- it ( "open() keeps focus for mobile time-only input" , ( ) => {
1470+ it ( "open() keeps custom picker for mobile time-only input" , ( ) => {
14561471 mockAgent = "Android" ;
14571472 createInstance ( {
14581473 enableTime : true ,
14591474 noCalendar : true ,
14601475 } ) ;
14611476
1462- const mobileInput = fp . mobileInput as HTMLInputElement ;
1463- const focusSpy = jest . spyOn ( mobileInput , "focus" ) ;
1464- const clickSpy = jest . spyOn ( mobileInput , "click" ) ;
1465-
1466- fp . open ( ) ;
1467-
1468- expect ( focusSpy ) . toHaveBeenCalled ( ) ;
1469- expect ( clickSpy ) . toHaveBeenCalled ( ) ;
1477+ expect ( fp . isMobile ) . toBe ( false ) ;
1478+ expect ( fp . mobileInput ) . toBeUndefined ( ) ;
14701479 } ) ;
14711480 } ) ;
14721481 } ) ;
@@ -1547,6 +1556,219 @@ describe("flatpickr", () => {
15471556 expect ( fp . hourElement . value ) . toEqual ( "09" ) ;
15481557 } ) ;
15491558
1559+ it ( "renders accessible time wheel popover" , ( ) => {
1560+ createInstance ( {
1561+ enableTime : true ,
1562+ } ) ;
1563+
1564+ const trigger = fp . timeContainer ?. querySelector (
1565+ ".flatpickr-time-wheel-trigger"
1566+ ) as HTMLElement | null ;
1567+ const popover = fp . timeContainer ?. querySelector (
1568+ ".flatpickr-time-wheel-popover"
1569+ ) as HTMLElement | null ;
1570+
1571+ expect ( trigger ) . toBeTruthy ( ) ;
1572+ expect ( popover ) . toBeTruthy ( ) ;
1573+ expect ( popover ?. getAttribute ( "role" ) ) . toBe ( "dialog" ) ;
1574+ expect ( popover ?. hasAttribute ( "hidden" ) ) . toBe ( true ) ;
1575+
1576+ if ( ! trigger || ! popover ) return ;
1577+
1578+ simulate ( "click" , trigger , { which : 1 } , MouseEvent ) ;
1579+ expect ( popover . hasAttribute ( "hidden" ) ) . toBe ( false ) ;
1580+
1581+ const done = popover . querySelector (
1582+ ".flatpickr-time-wheel-done"
1583+ ) as HTMLElement | null ;
1584+ expect ( done ) . toBeTruthy ( ) ;
1585+ if ( ! done ) return ;
1586+
1587+ simulate ( "click" , done , { which : 1 } , MouseEvent ) ;
1588+ expect ( popover . hasAttribute ( "hidden" ) ) . toBe ( true ) ;
1589+ } ) ;
1590+
1591+ it ( "time wheel popover respects minuteIncrement" , ( ) => {
1592+ createInstance ( {
1593+ enableTime : true ,
1594+ minuteIncrement : 15 ,
1595+ } ) ;
1596+
1597+ const minuteOptions = Array . from (
1598+ fp . timeContainer ?. querySelectorAll (
1599+ ".flatpickr-time-wheel-minutes .flatpickr-time-wheel-option"
1600+ ) || [ ]
1601+ ) . map ( ( el ) => ( el as HTMLElement ) . textContent ?. trim ( ) ) ;
1602+
1603+ expect ( minuteOptions ) . toEqual ( [ "00" , "15" , "30" , "45" ] ) ;
1604+ } ) ;
1605+
1606+ it ( "time wheel popover updates minute value on click" , ( ) => {
1607+ createInstance ( {
1608+ enableTime : true ,
1609+ defaultDate : "2017-1-1 10:00" ,
1610+ } ) ;
1611+
1612+ const targetOption = fp . timeContainer ?. querySelector (
1613+ ".flatpickr-time-wheel-minutes .flatpickr-time-wheel-option[data-value='30']"
1614+ ) as HTMLElement | null ;
1615+
1616+ expect ( targetOption ) . toBeTruthy ( ) ;
1617+ if ( ! targetOption || ! fp . minuteElement ) return ;
1618+
1619+ simulate ( "click" , targetOption , { which : 1 } , MouseEvent ) ;
1620+ expect ( fp . minuteElement . value ) . toBe ( "30" ) ;
1621+ } ) ;
1622+
1623+ it ( "time wheel popover supports arrow navigation and escape" , ( ) => {
1624+ createInstance ( {
1625+ enableTime : true ,
1626+ defaultDate : "2017-1-1 10:00" ,
1627+ } ) ;
1628+
1629+ const trigger = fp . timeContainer ?. querySelector (
1630+ ".flatpickr-time-wheel-trigger"
1631+ ) as HTMLElement | null ;
1632+ const popover = fp . timeContainer ?. querySelector (
1633+ ".flatpickr-time-wheel-popover"
1634+ ) as HTMLElement | null ;
1635+
1636+ expect ( trigger ) . toBeTruthy ( ) ;
1637+ expect ( popover ) . toBeTruthy ( ) ;
1638+ if ( ! trigger || ! popover || ! fp . hourElement || ! fp . minuteElement ) return ;
1639+
1640+ simulate ( "click" , trigger , { which : 1 } , MouseEvent ) ;
1641+
1642+ const selectedHour = popover . querySelector (
1643+ ".flatpickr-time-wheel-hours .flatpickr-time-wheel-option.is-selected"
1644+ ) as HTMLElement | null ;
1645+ expect ( selectedHour ) . toBeTruthy ( ) ;
1646+ if ( ! selectedHour ) return ;
1647+
1648+ simulate ( "keydown" , selectedHour , { key : "ArrowDown" } , KeyboardEvent ) ;
1649+ expect ( fp . hourElement . value ) . toBe ( "11" ) ;
1650+
1651+ simulate ( "keydown" , selectedHour , { key : "Tab" } , KeyboardEvent ) ;
1652+ const selectedMinute = popover . querySelector (
1653+ ".flatpickr-time-wheel-minutes .flatpickr-time-wheel-option.is-selected"
1654+ ) as HTMLElement | null ;
1655+ expect ( document . activeElement ) . toBe ( selectedMinute ) ;
1656+
1657+ if ( ! selectedMinute ) return ;
1658+ simulate ( "keydown" , selectedMinute , { key : "Escape" } , KeyboardEvent ) ;
1659+ expect ( popover . hasAttribute ( "hidden" ) ) . toBe ( true ) ;
1660+ expect ( document . activeElement ) . toBe ( trigger ) ;
1661+ } ) ;
1662+
1663+ it ( "enter on time trigger opens popover instead of closing calendar" , ( ) => {
1664+ createInstance ( {
1665+ enableTime : true ,
1666+ } ) ;
1667+
1668+ fp . open ( ) ;
1669+
1670+ const trigger = fp . timeContainer ?. querySelector (
1671+ ".flatpickr-time-wheel-trigger"
1672+ ) as HTMLElement | null ;
1673+ const popover = fp . timeContainer ?. querySelector (
1674+ ".flatpickr-time-wheel-popover"
1675+ ) as HTMLElement | null ;
1676+
1677+ expect ( trigger ) . toBeTruthy ( ) ;
1678+ expect ( popover ) . toBeTruthy ( ) ;
1679+ if ( ! trigger || ! popover ) return ;
1680+
1681+ trigger . focus ( ) ;
1682+ simulate ( "keydown" , trigger , { keyCode : 13 } , KeyboardEvent ) ;
1683+
1684+ expect ( fp . isOpen ) . toBe ( true ) ;
1685+ expect ( popover . hasAttribute ( "hidden" ) ) . toBe ( false ) ;
1686+ } ) ;
1687+
1688+ it ( "enter on done closes only the popover" , ( ) => {
1689+ createInstance ( {
1690+ enableTime : true ,
1691+ } ) ;
1692+
1693+ fp . open ( ) ;
1694+
1695+ const trigger = fp . timeContainer ?. querySelector (
1696+ ".flatpickr-time-wheel-trigger"
1697+ ) as HTMLElement | null ;
1698+ const popover = fp . timeContainer ?. querySelector (
1699+ ".flatpickr-time-wheel-popover"
1700+ ) as HTMLElement | null ;
1701+ const done = fp . timeContainer ?. querySelector (
1702+ ".flatpickr-time-wheel-done"
1703+ ) as HTMLElement | null ;
1704+
1705+ expect ( trigger ) . toBeTruthy ( ) ;
1706+ expect ( popover ) . toBeTruthy ( ) ;
1707+ expect ( done ) . toBeTruthy ( ) ;
1708+ if ( ! trigger || ! popover || ! done ) return ;
1709+
1710+ simulate ( "click" , trigger , { which : 1 } , MouseEvent ) ;
1711+ done . focus ( ) ;
1712+ simulate ( "keydown" , done , { keyCode : 13 } , KeyboardEvent ) ;
1713+
1714+ expect ( fp . isOpen ) . toBe ( true ) ;
1715+ expect ( popover . hasAttribute ( "hidden" ) ) . toBe ( true ) ;
1716+ expect ( document . activeElement ) . toBe ( trigger ) ;
1717+ } ) ;
1718+
1719+ it ( "time only renders the wheel popover directly" , ( ) => {
1720+ createInstance ( {
1721+ enableTime : true ,
1722+ noCalendar : true ,
1723+ } ) ;
1724+
1725+ const trigger = fp . timeContainer ?. querySelector (
1726+ ".flatpickr-time-wheel-trigger"
1727+ ) as HTMLElement | null ;
1728+ const popover = fp . timeContainer ?. querySelector (
1729+ ".flatpickr-time-wheel-popover"
1730+ ) as HTMLElement | null ;
1731+
1732+ expect ( trigger ) . toBeNull ( ) ;
1733+ expect ( popover ) . toBeTruthy ( ) ;
1734+ expect ( popover ?. hasAttribute ( "hidden" ) ) . toBe ( false ) ;
1735+ } ) ;
1736+
1737+ it ( "time only reopens the wheel popover after close" , ( ) => {
1738+ createInstance ( {
1739+ enableTime : true ,
1740+ noCalendar : true ,
1741+ } ) ;
1742+
1743+ const popover = fp . timeContainer ?. querySelector (
1744+ ".flatpickr-time-wheel-popover"
1745+ ) as HTMLElement | null ;
1746+ expect ( popover ) . toBeTruthy ( ) ;
1747+ if ( ! popover ) return ;
1748+
1749+ fp . close ( ) ;
1750+ expect ( popover . hasAttribute ( "hidden" ) ) . toBe ( true ) ;
1751+
1752+ fp . open ( ) ;
1753+ expect ( popover . hasAttribute ( "hidden" ) ) . toBe ( false ) ;
1754+ } ) ;
1755+
1756+ it ( "time only tabs from input into the wheel" , ( ) => {
1757+ createInstance ( {
1758+ enableTime : true ,
1759+ noCalendar : true ,
1760+ } ) ;
1761+
1762+ fp . _input . focus ( ) ;
1763+ simulate ( "keydown" , fp . _input , { keyCode : 9 } , KeyboardEvent ) ;
1764+
1765+ const selectedHour = fp . timeContainer ?. querySelector (
1766+ ".flatpickr-time-wheel-hours .flatpickr-time-wheel-option.is-selected"
1767+ ) as HTMLElement | null ;
1768+
1769+ expect ( document . activeElement ) . toBe ( selectedHour ) ;
1770+ } ) ;
1771+
15501772 it ( "time input respects minDate" , ( ) => {
15511773 createInstance ( {
15521774 enableTime : true ,
@@ -2117,6 +2339,23 @@ describe("flatpickr", () => {
21172339 expect ( fp . currentMonth ) . toEqual ( 3 ) ;
21182340 expect ( monthControl . value ) . toEqual ( "3" ) ;
21192341 } ) ;
2342+
2343+ it ( "datetime mode does not auto-focus time input after mouse date click" , ( ) => {
2344+ const fp = createInstance ( {
2345+ enableTime : true ,
2346+ } ) as Instance ;
2347+
2348+ fp . open ( ) ;
2349+
2350+ const hourFocusSpy = jest . spyOn (
2351+ fp . hourElement as HTMLInputElement ,
2352+ "focus"
2353+ ) ;
2354+
2355+ simulate ( "click" , fp . days . childNodes [ 15 ] , { which : 1 } , MouseEvent ) ;
2356+
2357+ expect ( hourFocusSpy ) . not . toHaveBeenCalled ( ) ;
2358+ } ) ;
21202359 } ) ;
21212360
21222361 describe ( "Localization" , ( ) => {
0 commit comments