UNPKG

vanillajs-datepicker

Version:

A vanilla JavaScript remake of bootstrap-datepicker for Bulma and other CSS frameworks

694 lines (546 loc) 24.4 kB
describe('mouse operation', function () { let input; before(function () { input = document.createElement('input'); testContainer.appendChild(input); }); after(function () { if (input.datepicker) { input.datepicker.destroy(); } testContainer.removeChild(input); }); it('picker hides if the input blurs after mouse is pressed outside the picker or the input', async function () { const outsider = document.createElement('p'); testContainer.appendChild(outsider); let {dp, picker} = createDP(input); input.focus(); simulant.fire(picker.querySelector('.dow'), 'mousedown'); input.blur(); expect(isVisible(picker), 'to be true'); input.focus(); simulant.fire(input, 'mousedown'); input.blur(); expect(isVisible(picker), 'to be true'); input.focus(); simulant.fire(outsider, 'mousedown'); input.blur(); expect(isVisible(picker), 'to be false'); // hide() should not called when picker is hidden // (issue #45) const spyHide = sinon.spy(dp, 'hide'); simulant.fire(outsider, 'mousedown'); input.blur(); expect(spyHide.called, 'to be false'); spyHide.restore(); input.focus(); dp.hide(); // picker shown programmatically should be closed by clicking outside // (issue #52) dp.show(); simulant.fire(outsider, 'mousedown'); input.blur(); expect(isVisible(picker), 'to be false'); // picker hides even when input is already unfocused dp.show(); input.blur(); simulant.fire(outsider, 'mousedown'); expect(isVisible(picker), 'to be false'); // picker hides reverting the input when invalid date is in the input (bugfix) input.focus(); input.value = '0/0/0'; simulant.fire(outsider, 'mousedown'); input.blur(); expect(isVisible(picker), 'to be false'); expect(input.value, 'to be', ''); // reverting the input also works when picker is already hidden input.focus(); input.value = '0/0/0'; dp.hide(); simulant.fire(outsider, 'mousedown'); input.blur(); expect(input.value, 'to be', ''); dp.destroy(); // works with shadow dom const testWrapper = document.createElement('test-wrapper'); testContainer.replaceChild(testWrapper, input); testWrapper.shadowRoot.appendChild(input); ({dp, picker} = createDP(input)); input.focus(); const isPickerVisible = () => new Promise((resolve) => { window.requestAnimationFrame(() => { resolve(isVisible(picker)); }); }); simulant.fire(picker.querySelector('.dow'), 'mousedown'); input.blur(); expect(await isPickerVisible(), 'to be true'); input.focus(); simulant.fire(input, 'mousedown'); input.blur(); expect(await isPickerVisible(), 'to be true'); input.focus(); simulant.fire(outsider, 'mousedown'); input.blur(); expect(await isPickerVisible(), 'to be false'); dp.destroy(); testContainer.replaceChild(input, testWrapper); testContainer.removeChild(outsider); return Promise.resolve(); }); it('selection is updated with input\'s value if the input blurs after mouse is pressed outside the input', function () { const clock = sinon.useFakeTimers({now: new Date(2020, 1, 14), shouldAdvanceTime: true}); const outsider = document.createElement('p'); testContainer.appendChild(outsider); const {dp, picker} = createDP(input); // when picker is shown input.focus(); input.value = 'foo'; simulant.fire(picker.querySelector('.dow'), 'mousedown'); input.blur(); expect(input.value, 'to be', 'foo'); input.focus(); simulant.fire(input, 'mousedown'); input.blur(); expect(input.value, 'to be', 'foo'); input.focus(); simulant.fire(outsider, 'mousedown'); input.blur(); expect(input.value, 'to be', '02/14/2020'); expect(dp.getDate().getTime(), 'to be', dateValue(2020, 1, 14)); // when picker is hidden input.focus(); input.value = '04/22/2020'; simulant.fire(outsider, 'mousedown'); input.blur(); expect(input.value, 'to be', '04/22/2020'); expect(dp.getDate().getTime(), 'to be', dateValue(2020, 3, 22)); input.focus(); input.value = ''; simulant.fire(outsider, 'mousedown'); input.blur(); expect(input.value, 'to be', ''); expect(dp.getDate(), 'to be undefined'); dp.destroy(); testContainer.removeChild(outsider); clock.restore(); }); it('picker shows up if input field is clicked wheh picker is hidden', function () { const {dp, picker} = createDP(input); // when input field is not focued simulant.fire(input, 'mousedown'); input.click(); expect(isVisible(picker), 'to be true'); dp.hide(); // when input has focus simulant.fire(input, 'mousedown'); input.click(); expect(isVisible(picker), 'to be true'); dp.destroy(); }); it('move of focus from input by clicking on picker is prevented by canceling mousedown event', function () { const {dp, picker} = createDP(input); const [viewSwitch, prevButton] = getParts(picker, ['.view-switch', '.prev-button']); const cells = getCells(picker); let event; const listener = ev => { event = ev; }; picker.addEventListener('mousedown', listener); input.focus(); simulant.fire(picker, 'mousedown'); expect(event.defaultPrevented, 'to be true'); event = undefined; simulant.fire(viewSwitch, 'mousedown'); expect(event.defaultPrevented, 'to be true'); event = undefined; simulant.fire(prevButton, 'mousedown'); expect(event.defaultPrevented, 'to be true'); event = undefined; simulant.fire(cells[11], 'mousedown'); expect(event.defaultPrevented, 'to be true'); dp.destroy(); }); describe('view-switch', function () { it('changes the view to the next greater one', function () { input.value = '04/22/2020'; const {dp, picker} = createDP(input); const viewSwitch = getViewSwitch(picker); dp.show(); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2020'); expect(input.value, 'to be', '04/22/2020'); let cells = getCells(picker); expect(cells.map(el => el.textContent), 'to equal', Datepicker.locales.en.monthsShort); expect(cells[3].classList.contains('selected'), 'to be true'); expect(cells[3].classList.contains('focused'), 'to be true'); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2020-2029'); expect(input.value, 'to be', '04/22/2020'); cells = getCells(picker); expect(cells, 'to have length', 12); expect(cells[0].textContent, 'to be', '2019'); expect(cells[0].classList.contains('prev'), 'to be true'); expect(cells[0].classList.contains('next'), 'to be false'); expect(cells[1].textContent, 'to be', '2020'); expect(cells[1].classList.contains('prev'), 'to be false'); expect(cells[1].classList.contains('next'), 'to be false'); expect(cells[10].textContent, 'to be', '2029'); expect(cells[10].classList.contains('prev'), 'to be false'); expect(cells[10].classList.contains('next'), 'to be false'); expect(cells[11].textContent, 'to be', '2030'); expect(cells[11].classList.contains('prev'), 'to be false'); expect(cells[11].classList.contains('next'), 'to be true'); expect(getCellIndices(cells, '.selected'), 'to equal', [1]); expect(getCellIndices(cells, '.focused'), 'to equal', [1]); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2000-2090'); expect(input.value, 'to be', '04/22/2020'); cells = getCells(picker); expect(cells, 'to have length', 12); expect(cells[0].textContent, 'to be', '1990'); expect(cells[0].classList.contains('prev'), 'to be true'); expect(cells[0].classList.contains('next'), 'to be false'); expect(cells[1].textContent, 'to be', '2000'); expect(cells[1].classList.contains('prev'), 'to be false'); expect(cells[1].classList.contains('next'), 'to be false'); expect(cells[10].textContent, 'to be', '2090'); expect(cells[10].classList.contains('prev'), 'to be false'); expect(cells[10].classList.contains('next'), 'to be false'); expect(cells[11].textContent, 'to be', '2100'); expect(cells[11].classList.contains('prev'), 'to be false'); expect(cells[11].classList.contains('next'), 'to be true'); expect(getCellIndices(cells, '.selected'), 'to equal', [3]); expect(getCellIndices(cells, '.focused'), 'to equal', [3]); expect(cells[3].textContent, 'to be', '2020'); // does nothig if the view has reached to the max view viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2000-2090'); dp.destroy(); input.value = ''; }); }); describe('prev-button', function () { it('changes the month/year/decade/century of the view to the previouse one', function () { input.value = '04/22/2020'; const {dp, picker} = createDP(input); const [viewSwitch, prevButton] = getParts(picker, ['.view-switch', '.prev-button']); dp.show(); prevButton.click(); expect(viewSwitch.textContent, 'to be', 'March 2020'); expect(input.value, 'to be', '04/22/2020'); // view date is changed to the same day of the previous month let cells = getCells(picker); expect(cells, 'to have length', 42); expect(cells[0].textContent, 'to be', '1'); expect(cells[0].classList.contains('prev'), 'to be false'); expect(cells[0].classList.contains('next'), 'to be false'); expect(cells[30].textContent, 'to be', '31'); expect(cells[30].classList.contains('prev'), 'to be false'); expect(cells[30].classList.contains('next'), 'to be false'); expect(cells[31].textContent, 'to be', '1'); expect(cells[31].classList.contains('prev'), 'to be false'); expect(cells[31].classList.contains('next'), 'to be true'); expect(cells[41].textContent, 'to be', '11'); expect(cells[41].classList.contains('prev'), 'to be false'); expect(cells[41].classList.contains('next'), 'to be true'); expect(getCellIndices(cells, '.selected'), 'to equal', []); expect(getCellIndices(cells, '.focused'), 'to equal', [21]); expect(cells[21].textContent, 'to be', '22'); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2020'); cells = getCells(picker); expect(getCellIndices(cells, '.selected'), 'to equal', [3]); expect(getCellIndices(cells, '.focused'), 'to equal', [2]); prevButton.click(); expect(viewSwitch.textContent, 'to be', '2019'); expect(input.value, 'to be', '04/22/2020'); // view date is changed to the same month of the previous year cells = getCells(picker); expect(cells.map(el => el.textContent), 'to equal', Datepicker.locales.en.monthsShort); expect(getCellIndices(cells, '.selected'), 'to equal', []); expect(getCellIndices(cells, '.focused'), 'to equal', [2]); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2010-2019'); cells = getCells(picker); expect(getCellIndices(cells, '.selected'), 'to equal', [11]); expect(cells[11].textContent, 'to be', '2020'); expect(getCellIndices(cells, '.focused'), 'to equal', [10]); expect(cells[10].textContent, 'to be', '2019'); prevButton.click(); expect(viewSwitch.textContent, 'to be', '2000-2009'); expect(input.value, 'to be', '04/22/2020'); cells = getCells(picker); expect(cells, 'to have length', 12); expect(cells[0].textContent, 'to be', '1999'); expect(cells[1].textContent, 'to be', '2000'); expect(cells[10].textContent, 'to be', '2009'); expect(cells[11].textContent, 'to be', '2010'); expect(getCellIndices(cells, '.selected'), 'to equal', []); expect(getCellIndices(cells, '.focused'), 'to equal', [10]); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2000-2090'); cells = getCells(picker); expect(getCellIndices(cells, '.selected'), 'to equal', [3]); expect(cells[3].textContent, 'to be', '2020'); expect(getCellIndices(cells, '.focused'), 'to equal', [1]); expect(cells[1].textContent, 'to be', '2000'); prevButton.click(); expect(viewSwitch.textContent, 'to be', '1900-1990'); expect(input.value, 'to be', '04/22/2020'); cells = getCells(picker); expect(cells, 'to have length', 12); expect(cells[0].textContent, 'to be', '1890'); expect(cells[1].textContent, 'to be', '1900'); expect(cells[10].textContent, 'to be', '1990'); expect(cells[11].textContent, 'to be', '2000'); expect(getCellIndices(cells, '.selected'), 'to equal', []); expect(getCellIndices(cells, '.focused'), 'to equal', [1]); dp.destroy(); input.value = ''; }); it('controls the view date in days view to be in the prev month when moving from a longer month to shorter', function () { input.value = '03/31/2019'; const {dp, picker} = createDP(input); const [viewSwitch, prevButton] = getParts(picker, ['.view-switch', '.prev-button']); dp.show(); prevButton.click(); expect(viewSwitch.textContent, 'to be', 'February 2019'); let cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [32]); expect(cells[32].textContent, 'to be', '28'); input.value = '03/31/2020'; dp.update(); prevButton.click(); expect(viewSwitch.textContent, 'to be', 'February 2020'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [34]); expect(cells[34].textContent, 'to be', '29'); input.value = '10/31/2020'; dp.update(); prevButton.click(); expect(viewSwitch.textContent, 'to be', 'September 2020'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [31]); expect(cells[31].textContent, 'to be', '30'); dp.destroy(); input.value = ''; }); it('becomes disabled if the view includes the decade/year/month/day of 0000-01-01', function () { input.value = '01/04/0000'; const {dp, picker} = createDP(input); const [viewSwitch, prevButton] = getParts(picker, ['.view-switch', '.prev-button']); dp.show(); expect(prevButton.disabled, 'to be true'); prevButton.click(); expect(viewSwitch.textContent, 'to be', 'January 0'); viewSwitch.click(); expect(prevButton.disabled, 'to be true'); prevButton.click(); expect(viewSwitch.textContent, 'to be', '0'); viewSwitch.click(); expect(prevButton.disabled, 'to be true'); prevButton.click(); expect(viewSwitch.textContent, 'to be', '0-9'); viewSwitch.click(); expect(prevButton.disabled, 'to be true'); prevButton.click(); expect(viewSwitch.textContent, 'to be', '0-90'); dp.destroy(); input.value = ''; }); }); describe('next-button', function () { it('changes the month/year/decade/century of the view to the next one', function () { input.value = '04/22/2020'; const {dp, picker} = createDP(input); const [viewSwitch, nextButton] = getParts(picker, ['.view-switch', '.next-button']); dp.show(); nextButton.click(); expect(viewSwitch.textContent, 'to be', 'May 2020'); expect(input.value, 'to be', '04/22/2020'); // view date is changed to the same day of the next month let cells = getCells(picker); expect(cells, 'to have length', 42); expect(cells[0].textContent, 'to be', '26'); expect(cells[0].classList.contains('prev'), 'to be true'); expect(cells[0].classList.contains('next'), 'to be false'); expect(cells[4].textContent, 'to be', '30'); expect(cells[4].classList.contains('prev'), 'to be true'); expect(cells[4].classList.contains('next'), 'to be false'); expect(cells[5].textContent, 'to be', '1'); expect(cells[5].classList.contains('prev'), 'to be false'); expect(cells[5].classList.contains('next'), 'to be false'); expect(cells[35].textContent, 'to be', '31'); expect(cells[35].classList.contains('prev'), 'to be false'); expect(cells[35].classList.contains('next'), 'to be false'); expect(cells[36].textContent, 'to be', '1'); expect(cells[36].classList.contains('prev'), 'to be false'); expect(cells[36].classList.contains('next'), 'to be true'); expect(cells[41].textContent, 'to be', '6'); expect(cells[41].classList.contains('prev'), 'to be false'); expect(cells[41].classList.contains('next'), 'to be true'); expect(getCellIndices(cells, '.selected'), 'to equal', []); expect(getCellIndices(cells, '.focused'), 'to equal', [26]); expect(cells[26].textContent, 'to be', '22'); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2020'); cells = getCells(picker); expect(getCellIndices(cells, '.selected'), 'to equal', [3]); expect(getCellIndices(cells, '.focused'), 'to equal', [4]); nextButton.click(); expect(viewSwitch.textContent, 'to be', '2021'); expect(input.value, 'to be', '04/22/2020'); // view date is changed to the same month of the previous year cells = getCells(picker); expect(cells.map(el => el.textContent), 'to equal', Datepicker.locales.en.monthsShort); expect(getCellIndices(cells, '.selected'), 'to equal', []); expect(getCellIndices(cells, '.focused'), 'to equal', [4]); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2020-2029'); cells = getCells(picker); expect(getCellIndices(cells, '.selected'), 'to equal', [1]); expect(getCellIndices(cells, '.focused'), 'to equal', [2]); expect(cells[1].textContent, 'to be', '2020'); expect(cells[2].textContent, 'to be', '2021'); nextButton.click(); expect(viewSwitch.textContent, 'to be', '2030-2039'); expect(input.value, 'to be', '04/22/2020'); cells = getCells(picker); expect(cells, 'to have length', 12); expect(cells[0].textContent, 'to be', '2029'); expect(cells[1].textContent, 'to be', '2030'); expect(cells[10].textContent, 'to be', '2039'); expect(cells[11].textContent, 'to be', '2040'); expect(getCellIndices(cells, '.selected'), 'to equal', []); expect(getCellIndices(cells, '.focused'), 'to equal', [2]); expect(cells[2].textContent, 'to be', '2031'); viewSwitch.click(); expect(viewSwitch.textContent, 'to be', '2000-2090'); cells = getCells(picker); expect(getCellIndices(cells, '.selected'), 'to equal', [3]); expect(getCellIndices(cells, '.focused'), 'to equal', [4]); expect(cells[3].textContent, 'to be', '2020'); expect(cells[4].textContent, 'to be', '2030'); nextButton.click(); expect(viewSwitch.textContent, 'to be', '2100-2190'); expect(input.value, 'to be', '04/22/2020'); cells = getCells(picker); expect(cells, 'to have length', 12); expect(cells[0].textContent, 'to be', '2090'); expect(cells[1].textContent, 'to be', '2100'); expect(cells[10].textContent, 'to be', '2190'); expect(cells[11].textContent, 'to be', '2200'); expect(getCellIndices(cells, '.selected'), 'to equal', []); expect(getCellIndices(cells, '.focused'), 'to equal', [4]); expect(cells[4].textContent, 'to be', '2130'); dp.destroy(); input.value = ''; }); it('controls the view date in days view to be in the next month when moving from a longer month to shorter', function () { input.value = '01/31/2019'; const {dp, picker} = createDP(input); const [viewSwitch, nextButton] = getParts(picker, ['.view-switch', '.next-button']); dp.show(); nextButton.click(); expect(viewSwitch.textContent, 'to be', 'February 2019'); let cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [32]); expect(cells[32].textContent, 'to be', '28'); input.value = '01/31/2020'; dp.update(); nextButton.click(); expect(viewSwitch.textContent, 'to be', 'February 2020'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [34]); expect(cells[34].textContent, 'to be', '29'); input.value = '08/31/2020'; dp.update(); nextButton.click(); expect(viewSwitch.textContent, 'to be', 'September 2020'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [31]); expect(cells[31].textContent, 'to be', '30'); dp.destroy(); input.value = ''; }); }); describe('datepicker-cell', function () { let clock; let dp; let picker; beforeEach(function () { clock = sinon.useFakeTimers({now: new Date(2020, 1, 14), shouldAdvanceTime: true}); ({dp, picker} = createDP(input)); dp.show(); }); afterEach(function () { dp.destroy(); clock.restore(); }); it('changes the selection to the clicked date if the current view = days', function () { const targetCell = getCells(picker)[19]; targetCell.click(); expect(dp.dates, 'to equal', [new Date(2020, 1, 14).getTime()]); expect(input.value, 'to be', '02/14/2020'); expect(getViewSwitch(picker).textContent, 'to be', 'February 2020'); expect(targetCell.classList.contains('selected'), 'to be true'); expect(targetCell.classList.contains('focused'), 'to be true'); dp.setDate({clear: true}); }); it('also changes the month of the view if a date of previous or next month is clicked', function () { const viewSwitch = getViewSwitch(picker); getCells(picker)[1].click(); expect(dp.dates, 'to equal', [new Date(2020, 0, 27).getTime()]); expect(input.value, 'to be', '01/27/2020'); expect(viewSwitch.textContent, 'to be', 'January 2020'); let cells = getCells(picker); expect(getCellIndices(cells, '.selected'), 'to equal', [29]); expect(getCellIndices(cells, '.focused'), 'to equal', [29]); expect(cells[29].textContent, 'to be', '27'); expect(cells[40].textContent, 'to be', '7'); cells[40].click(); expect(dp.dates, 'to equal', [dateValue(2020, 1, 7)]); expect(input.value, 'to be', '02/07/2020'); expect(viewSwitch.textContent, 'to be', 'February 2020'); cells = getCells(picker); expect(getCellIndices(cells, '.selected'), 'to equal', [12]); expect(getCellIndices(cells, '.focused'), 'to equal', [12]); expect(cells[12].textContent, 'to be', '7'); dp.setDate({clear: true}); }); it('changes the view year or month to the clicked one and moves to the next minor view if the current view != days', function () { const viewSwitch = getViewSwitch(picker); viewSwitch.click(); viewSwitch.click(); viewSwitch.click(); viewSwitch.click(); // on decades view: 2000-2090 let cells = getCells(picker); // click "2010" cells[2].click(); expect(dp.dates, 'to equal', []); expect(input.value, 'to be', ''); expect(getViewSwitch(picker).textContent, 'to be', '2010-2019'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [1]); expect(cells[1].textContent, 'to be', '2010'); // click "2017" cells[8].click(); expect(dp.dates, 'to equal', []); expect(input.value, 'to be', ''); expect(getViewSwitch(picker).textContent, 'to be', '2017'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [1]); expect(cells[1].textContent, 'to be', 'Feb'); // click "Oct" cells[9].click(); expect(dp.dates, 'to equal', []); expect(input.value, 'to be', ''); expect(getViewSwitch(picker).textContent, 'to be', 'October 2017'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [13]); expect(cells[13].textContent, 'to be', '14'); }); }); });