UNPKG

vanillajs-datepicker

Version:

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

815 lines (606 loc) 23.9 kB
describe('options', function () { let clock; let input; beforeEach(function () { clock = sinon.useFakeTimers({now: new Date(2020, 1, 14), shouldAdvanceTime: true}); input = document.createElement('input'); testContainer.appendChild(input); }); afterEach(function () { if (input.datepicker) { input.datepicker.destroy(); } testContainer.removeChild(input); clock.restore(); }); describe('autohide', function () { it('makes the picker hide automatically on selection when true', function () { const {dp, picker} = createDP(input, {autohide: true}); dp.show(); // by satDate() dp.setDate('2/4/2020'); expect(isVisible(picker), 'to be false'); dp.show(); // by click on day cell getCells(picker)[25].click(); expect(isVisible(picker), 'to be false'); dp.show(); // by typing enter key in edit mode dp.enterEditMode(); input.value = '2/14/2020'; simulant.fire(input, 'keydown', {key: 'Enter'}); expect(isVisible(picker), 'to be false'); // focus is kept on input field after auto-hidng by clicking day cell // (issue #21) dp.show(); getCells(picker)[25].click(); expect(document.activeElement, 'to be', input); dp.destroy(); input.value = ''; }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.setOptions({autohide: true}); dp.show(); dp.setDate('2/4/2020'); expect(isVisible(picker), 'to be false'); dp.setOptions({autohide: false}); dp.show(); dp.setDate('2/14/2020'); expect(isVisible(picker), 'to be true'); dp.destroy(); input.value = ''; }); }); describe('buttonClass', function () { it('specifies the main class used for the button elements', function () { const {dp, picker} = createDP(input, {buttonClass: 'btn'}); const [viewSwitch, prevButton, nextButton, todayButton, clearButton] = getParts(picker, [ '.view-switch', '.prev-button', '.next-button', '.today-button', '.clear-button', ]); expect(viewSwitch.className, 'to be', 'btn view-switch'); expect(prevButton.className, 'to be', 'btn prev-button prev-btn'); expect(nextButton.className, 'to be', 'btn next-button next-btn'); expect(todayButton.className, 'to be', 'btn today-button today-btn'); expect(clearButton.className, 'to be', 'btn clear-button clear-btn'); dp.destroy(); }); it('cannot be update with setOptions()', function () { const {dp, picker} = createDP(input); dp.setOptions({buttonClass: 'btn'}); const [viewSwitch, prevButton, nextButton, todayButton, clearButton] = getParts(picker, [ '.view-switch', '.prev-button', '.next-button', '.today-button', '.clear-button', ]); expect(viewSwitch.className, 'to be', 'button view-switch'); expect(prevButton.className, 'to be', 'button prev-button prev-btn'); expect(nextButton.className, 'to be', 'button next-button next-btn'); expect(todayButton.className, 'to be', 'button today-button today-btn'); expect(clearButton.className, 'to be', 'button clear-button clear-btn'); dp.destroy(); }); }); describe('container', function () { let foo; beforeEach(function () { // foo = parseHTML('<div id="foo"><div>').firstChild; foo = document.createElement('div'); foo.id = 'foo'; testContainer.appendChild(foo); }); afterEach(function () { testContainer.removeChild(foo); }); it('specifies the element to attach the picker', function () { // with css selector let {dp, picker} = createDP(input, {container: '#foo'}); expect(picker.parentElement, 'to be', foo); dp.destroy(); // the picker should be removed from the container when destroyed expect(Array.from(foo.children).includes(picker), 'to be false'); ({dp, picker} = createDP(input, {container: 'body'})); expect(picker.parentElement, 'to be', document.body); dp.destroy(); expect(Array.from(document.body.children).includes(picker), 'to be false'); // with DOM ({dp, picker} = createDP(input, {container: foo})); expect(picker.parentElement, 'to be', foo); dp.destroy(); ({dp, picker} = createDP(input, {container: document.body})); expect(picker.parentElement, 'to be', document.body); dp.destroy(); }); it('cannot be update with setOptions()', function () { const dp = new Datepicker(input); dp.setOptions({container: '#foo'}); expect(document.querySelector('.datepicker').parentElement, 'to be', input.parentElement); dp.destroy(); }); describe('picker\'s text direction', function () { it('is adjussted to match the input field in the same way as without container', function (done) { // input's direction differs from the document's and the container's input.dir = 'rtl'; const {dp, picker} = createDP(input, {container: foo}); dp.show(); expect(picker.dir, 'to be', 'rtl'); dp.hide(); // container's direction becomes the same as the input's but differnet from the document's foo.dir = 'rtl'; dp.show(); expect(picker.hasAttribute('dir'), 'to be false'); dp.hide(); input.removeAttribute('dir'); foo.removeAttribute('dir'); // container's direction differs from the document's and input's const htmlElem = document.querySelector('html'); htmlElem.dir = 'rtl'; foo.style.direction = 'ltr'; dp.show(); expect(picker.dir, 'to be', 'rtl'); dp.hide(); // container's direction becomes the same as the document's and input's foo.removeAttribute('style'); dp.show(); expect(picker.hasAttribute('dir'), 'to be false'); dp.destroy(); htmlElem.removeAttribute('dir'); htmlElem.style.direction = 'ltr'; const checkDirChange = () => { if (window.getComputedStyle(htmlElem).direction === 'ltr') { htmlElem.removeAttribute('style'); done(); } else { setTimeout(checkDirChange, 10); } }; checkDirChange(); }); }); }); describe('daysOfWeekHighlighted', function () { const highlightedCellIndices = (picker) => { const cells = getCells(picker); return getCellIndices(cells, '.highlighted'); }; const highlighted1stWeekIndices = picker => highlightedCellIndices(picker).filter(val => val < 7); it('specifies the days of week to highlight by dey numbers', function () { const {dp, picker} = createDP(input, {daysOfWeekHighlighted: [1, 5]}); dp.show(); expect(highlightedCellIndices(picker), 'to equal', [1, 5, 8, 12, 15, 19, 22, 26, 29, 33, 36, 40]); const viewSwitch = getViewSwitch(picker); // months view viewSwitch.click(); expect(highlightedCellIndices(picker), 'to equal', []); // years view viewSwitch.click(); expect(highlightedCellIndices(picker), 'to equal', []); // decades view viewSwitch.click(); expect(highlightedCellIndices(picker), 'to equal', []); dp.destroy(); }); it('can contain values of 0 - 6 and max 6 items', function () { const {dp, picker} = createDP(input, {daysOfWeekHighlighted: [0, -1, 1, 2, 3, 2, 4, 5, 6, 7]}); dp.show(); expect(highlighted1stWeekIndices(picker), 'to equal', [0, 1, 2, 3, 4, 5]); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.setOptions({daysOfWeekHighlighted: [6, 0, 3]}); dp.show(); expect(highlighted1stWeekIndices(picker), 'to equal', [0, 3, 6]); dp.setOptions({daysOfWeekHighlighted: []}); expect(highlightedCellIndices(picker), 'to equal', []); dp.destroy(); }); }); describe('defaultViewDate', function () { it('specifies the start view date in the case no selection is made', function () { const date = new Date(1984, 0, 24); const {dp, picker} = createDP(input, {defaultViewDate: date}); dp.show(); expect(getViewSwitch(picker).textContent, 'to be', 'January 1984'); let cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [23]); expect(cells[23].textContent, 'to be', '24'); dp.setDate('7/4/2020'); dp.setDate({clear: true}); expect(getViewSwitch(picker).textContent, 'to be', 'January 1984'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [23]); expect(cells[23].textContent, 'to be', '24'); picker.querySelector('.prev-button').click(); dp.hide(); dp.show(); expect(getViewSwitch(picker).textContent, 'to be', 'January 1984'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [23]); expect(cells[23].textContent, 'to be', '24'); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.show(); dp.setOptions({defaultViewDate: new Date(1984, 0, 24)}); dp.hide(); dp.show(); expect(getViewSwitch(picker).textContent, 'to be', 'January 1984'); let cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [23]); expect(cells[23].textContent, 'to be', '24'); dp.setOptions({defaultViewDate: new Date(2007, 5, 29)}); dp.setDate('7/4/2020'); dp.setDate({clear: true}); expect(getViewSwitch(picker).textContent, 'to be', 'June 2007'); cells = getCells(picker); expect(getCellIndices(cells, '.focused'), 'to equal', [33]); expect(cells[33].textContent, 'to be', '29'); dp.destroy(); }); }); describe('disableTouchKeyboard', function () { const ontouchstartSupported = 'ontouchstart' in document; before(function () { if (!ontouchstartSupported) { document.ontouchstart = null; } }); after(function () { if (!ontouchstartSupported) { delete document.ontouchstart; } }); it('unfocuses the input after showing the picker', function () { const dp = new Datepicker(input, {disableTouchKeyboard: true}); input.focus(); expect(document.activeElement, 'not to be', input); dp.hide(); // input is unfocused when tapped to shou picker (bugfix) simulant.fire(input, 'mousedown'); input.focus(); input.click(); expect(document.activeElement, 'not to be', input); dp.destroy(); }); it('prevents the input from getting focus after an element in the picker is clicked', function () { const {dp, picker} = createDP(input, {disableTouchKeyboard: true}); const [viewSwitch, prevButton] = getParts(picker, ['.view-switch', '.prev-button']); dp.show(); input.blur(); prevButton.click(); expect(document.activeElement, 'not to be', input); simulant.fire(getCells(picker)[15], 'click'); expect(document.activeElement, 'not to be', input); viewSwitch.click(); expect(document.activeElement, 'not to be', input); simulant.fire(getCells(picker)[6], 'click'); expect(document.activeElement, 'not to be', input); dp.destroy(); }); it('is ignored if the browser does not support document.ontouchstart', function () { if (ontouchstartSupported) { return; } delete document.ontouchstart; const {dp, picker} = createDP(input, {disableTouchKeyboard: true}); const [viewSwitch, prevButton] = getParts(picker, ['.view-switch', '.prev-button']); input.focus(); expect(document.activeElement, 'to be', input); dp.hide(); input.blur(); simulant.fire(input, 'mousedown'); input.focus(); input.click(); expect(document.activeElement, 'to be', input); prevButton.click(); expect(document.activeElement, 'to be', input); simulant.fire(getCells(picker)[15], 'click'); expect(document.activeElement, 'to be', input); viewSwitch.click(); expect(document.activeElement, 'to be', input); simulant.fire(getCells(picker)[6], 'click'); expect(document.activeElement, 'to be', input); dp.destroy(); document.ontouchstart = null; }); it('can be updated with setOptions()', function () { const dp = new Datepicker(input); dp.setOptions({disableTouchKeyboard: true}); input.focus(); expect(document.activeElement, 'not to be', input); dp.hide(); dp.setOptions({disableTouchKeyboard: false}); input.focus(); expect(document.activeElement, 'to be', input); dp.destroy(); }); }); describe('enableOnReadonly', function () { beforeEach(function () { input.readOnly = true; }); after(function () { input.readOnly = false; }); it('disables the picker of the input with readonly attribute to be shown when false', function () { const {dp, picker} = createDP(input, {enableOnReadonly: false}); input.focus(); expect(isVisible(picker), 'to be false'); dp.show(); expect(isVisible(picker), 'to be false'); input.blur(); input.readOnly = false; input.focus(); expect(isVisible(picker), 'to be true'); dp.hide(); dp.show(); expect(isVisible(picker), 'to be true'); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.show(); expect(isVisible(picker), 'to be true'); dp.hide(); dp.setOptions({enableOnReadonly: false}); dp.show(); expect(isVisible(picker), 'to be false'); dp.destroy(); }); }); describe('nextArrow', function () { it('specifies the label of the next button in HTML (or plain text)', function () { const html = '<i class="icn icn-arrow-right"></i>'; const {dp, picker} = createDP(input, {nextArrow: html}); const nextButton = picker.querySelector('.next-button'); dp.show(); expect(nextButton.innerHTML, 'to be', html); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); const nextButton = picker.querySelector('.next-button'); dp.setOptions({nextArrow: 'N'}); dp.show(); expect(nextButton.textContent, 'to be', 'N'); dp.setOptions({nextArrow: '>'}); expect(nextButton.textContent, 'to be', '>'); dp.destroy(); }); }); describe('prevArrow', function () { it('specifies the label of the next button in HTML (or plain text)', function () { const html = '<i class="icn icn-arrow-left"></i>'; const {dp, picker} = createDP(input, {prevArrow: html}); const prevButton = picker.querySelector('.prev-button'); dp.show(); expect(prevButton.innerHTML, 'to be', html); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); const prevButton = picker.querySelector('.prev-button'); dp.setOptions({prevArrow: 'P'}); dp.show(); expect(prevButton.textContent, 'to be', 'P'); dp.setOptions({prevArrow: '<'}); expect(prevButton.textContent, 'to be', '<'); dp.destroy(); }); }); describe('showDaysOfWeek', function () { it('hides day names of week when false', function () { const {dp, picker} = createDP(input, {showDaysOfWeek: false}); dp.show(); expect(isVisible(picker.querySelector('.days-of-week')), 'to be false'); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.setOptions({showDaysOfWeek: false}); dp.show(); expect(isVisible(picker.querySelector('.days-of-week')), 'to be false'); dp.setOptions({showDaysOfWeek: true}); expect(isVisible(picker.querySelector('.days-of-week')), 'to be true'); dp.destroy(); }); }); describe('showOnClick', function () { it('disables the picker to auto-open on clicking input when false', function () { const {dp, picker} = createDP(input, {showOnClick: false}); input.focus(); dp.hide(); simulant.fire(input, 'mousedown'); input.click(); expect(isVisible(picker), 'to be false'); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.setOptions({showOnClick: false}); input.focus(); dp.hide(); simulant.fire(input, 'mousedown'); input.click(); expect(isVisible(picker), 'to be false'); dp.setOptions({showOnClick: true}); simulant.fire(input, 'mousedown'); input.click(); expect(isVisible(picker), 'to be true'); dp.destroy(); }); }); describe('showOnFocus', function () { it('disables the picker to auto-open on focus when false', function () { const {dp, picker} = createDP(input, {showOnFocus: false}); input.focus(); expect(isVisible(picker), 'to be false'); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.setOptions({showOnFocus: false}); input.focus(); expect(isVisible(picker), 'to be false'); input.blur(); dp.setOptions({showOnFocus: true}); input.focus(); expect(isVisible(picker), 'to be true'); dp.destroy(); }); }); describe('title', function () { it('specifies the title of the picker and shows it when not empty', function () { const {dp, picker} = createDP(input, {title: 'Foo Bar'}); const title = picker.querySelector('.datepicker-title'); dp.show(); expect(title.textContent, 'to be', 'Foo Bar'); expect(isVisible(title), 'to be true'); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); const title = picker.querySelector('.datepicker-title'); dp.setOptions({title: 'My Datepicker'}); dp.show(); expect(title.textContent, 'to be', 'My Datepicker'); expect(isVisible(title), 'to be true'); dp.setOptions({title: ''}); expect(title.textContent, 'to be', ''); expect(isVisible(title), 'to be false'); dp.destroy(); }); }); describe('todayHighlight', function () { it('highlights the current date in days view when true', function () { const {dp, picker} = createDP(input, {todayHighlight: true}); const viewSwitch = getViewSwitch(picker); dp.show(); let cells = getCells(picker); expect(getCellIndices(cells, '.today'), 'to equal', [19]); picker.querySelector('.prev-button').click(); expect(getCellIndices(getCells(picker), '.today'), 'to equal', []); picker.querySelector('.next-button').click(); viewSwitch.click(); expect(getCellIndices(getCells(picker), '.today'), 'to equal', []); viewSwitch.click(); expect(getCellIndices(getCells(picker), '.today'), 'to equal', []); viewSwitch.click(); expect(getCellIndices(getCells(picker), '.today'), 'to equal', []); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.setOptions({todayHighlight: true}); dp.show(); let cells = getCells(picker); expect(getCellIndices(cells, '.today'), 'to equal', [19]); dp.setOptions({todayHighlight: false}); cells = getCells(picker); expect(getCellIndices(cells, '.today'), 'to equal', []); dp.destroy(); }); }); describe('updateOnBlur', function () { it('discards unparsed input on losing focus when false', function () { const outsider = document.createElement('p'); testContainer.appendChild(outsider); const {dp, picker} = createDP(input, {updateOnBlur: false}); input.focus(); input.value = 'foo'; // on tab key press simulant.fire(input, 'keydown', {key: 'Tab'}); input.blur(); expect(input.value, 'to be', ''); dp.setDate('04/22/2020'); input.focus(); dp.enterEditMode(); input.value = 'foo'; simulant.fire(input, 'keydown', {key: 'Tab'}); input.blur(); expect(input.value, 'to be', '04/22/2020'); // on click outside 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', '04/22/2020'); dp.setDate({clear: true}); input.focus(); input.value = 'foo'; simulant.fire(outsider, 'mousedown'); input.blur(); expect(input.value, 'to be', ''); dp.destroy(); testContainer.removeChild(outsider); }); it('can be updated with setOptions()', function () { const dp = new Datepicker(input); dp.setOptions({updateOnBlur: false}); input.focus(); input.value = '04/22/2020'; simulant.fire(input, 'keydown', {key: 'Tab'}); input.blur(); expect(input.value, 'to be', ''); dp.setOptions({updateOnBlur: true}); input.focus(); input.value = '04/22/2020'; simulant.fire(input, 'keydown', {key: 'Tab'}); input.blur(); expect(input.value, 'to be', '04/22/2020'); dp.destroy(); }); }); describe('weekStart', function () { const getDayNames = (picker) => { const daysOfWeek = picker.querySelector('.days-of-week'); return Array.from(daysOfWeek.children).map(el => el.textContent); }; const getDatesInColumn = (picker, colIndex) => { const cells = getCells(picker); return cells.reduce((dates, el, ix) => { if (ix % 7 === colIndex) { dates.push(el.textContent); } return dates; }, []); }; it('specifies the day of week to display in the first column', function () { const {dp, picker} = createDP(input, {weekStart: 1}); dp.show(); expect(getDayNames(picker), 'to equal', ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']); expect(getDatesInColumn(picker, 0), 'to equal', ['27', '3', '10', '17', '24', '2']); expect(getDatesInColumn(picker, 6), 'to equal', ['2', '9', '16', '23', '1', '8']); dp.destroy(); }); it('can be updated with setOptions()', function () { const {dp, picker} = createDP(input); dp.setOptions({weekStart: 4}); dp.show(); expect(getDayNames(picker), 'to equal', ['Th', 'Fr', 'Sa', 'Su', 'Mo', 'Tu', 'We']); expect(getDatesInColumn(picker, 0), 'to equal', ['30', '6', '13', '20', '27', '5']); expect(getDatesInColumn(picker, 6), 'to equal', ['5', '12', '19', '26', '4', '11']); dp.setOptions({weekStart: 0}); expect(getDayNames(picker), 'to equal', ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']); expect(getDatesInColumn(picker, 0), 'to equal', ['26', '2', '9', '16', '23', '1']); expect(getDatesInColumn(picker, 6), 'to equal', ['1', '8', '15', '22', '29', '7']); dp.destroy(); }); }); });