A versatile date picker with day and month selection modes, multiple selection support, and various formatting options.
Great for booking systems! If users don't have a specific date in mind, they can easily pick a few different dates, a whole month, or even several months at once (with a maximum limit)
- DatepickerTabs
- Features
- Basic Usage
- Options
- Date Formatting
- Methods
- Events
- Examples
- Browser Support
- How to Rebuild Styles
- Best Practices
- Testing
- Future Work
- Day and Month selection modes
- Single or multiple selection
- Format customization
- Min/Max date constraints
- Max month selection limit
- Display type options: 'tabs', 'day', or 'month'
- Saturday-only selection for specific use cases
- Cookie-based mode persistence
- Tooltip overlay positioning
- Mobile-friendly design
- Automatic container creation
- Support for multiple instances with class selectors
git clone https://github.com/swayoleg/datepicker-tabs.git
Clone or download the packege from GitHub. Include css and js from dist folder to your page:
<link rel="stylesheet" href="dist/css/datepicker-tabs.min.css">
<script src="dist/js/datepicker-tabs.min.js"></script>
Alternatevly you can use JSDeliver
with certain version (1.0.4 in example):
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/swayoleg/[email protected]/dist/css/datepicker-tabs.min.css">
<script src="https://cdn.jsdelivr.net/gh/swayoleg/[email protected]/dist/js/datepicker-tabs.min.js"></script>
or with latest develop:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/swayoleg/datepicker-tabs@develop/dist/css/datepicker-tabs.min.css">
<script src="https://cdn.jsdelivr.net/gh/swayoleg/datepicker-tabs@develop/dist/js/datepicker-tabs.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/datepicker-tabs@latest/dist/css/datepicker-tabs.min.css">
<script src="https://unpkg.com/datepicker-tabs@latest/dist/js/datepicker-tabs.min.js"></script>
npm install datepicker-tabs
Examples can be found in index.html file or at Live Demo Page
// Initialize on a single input with ID
const picker = new DatepickerTabs('#date-input');
// Initialize on multiple inputs with class
const pickers = new DatepickerTabs('.date-input-class');
You can customize the date picker by passing an options object:
const picker = new DatepickerTabs('#date-input', {
// Basic configuration
mode: 'month', // 'day' or 'month' mode
displayType: 'tabs', // Display as 'tabs', 'day', or 'month'
// Selection options
multipleDays: false, // Allow multiple day selection
multipleMonths: true, // Allow multiple month selection
maxMonthSelection: 6, // Max selectable months (when multipleMonths=true)
// Date range options
startDate: null, // Initial selected date
minDate: null, // Minimum selectable date
maxDate: new Date(2026, 11, 31), // Maximum selectable date
futureSaturdaysOnly: true, // Only enable future Saturdays in day mode
disabledDaysOfWeek: [], // Array of days of week to disable (0-6, where 0 is Sunday)
disabledDates: [], //Array of specific dates to disable (Date objects or date strings in various formats)
startWeekOnMonday: false, // Option to start the week on Monday instead of Sunday
// Localization
monthNames: ['January', 'February'], // Custom month names
dayNames: ['Sun', 'Mon'], // Custom day names
dateFormat: 'DD/MM/YYYY', // Date display format
monthFormat: 'MMM YYYY', // Month display format
// UI settings
position: 'bottom', // 'bottom' or 'top' position
zIndex: 9999, // Picker z-index
// Persistence
cookieName: 'datepickerTabsMode',// Cookie name for mode storage
backwardsYearsOffset: 5, // How many year offset render backwards in years selectbox. If now 2025 it will render from 2020
forwardsYearsOffset: 5, // How many year offset render forwards in years selectbox. If now 2025 it will render till 2030
// Callbacks
onDateChange: function(date) { // Date selection callback
console.log('Selected date:', date);
},
containerId: '', //Custom container ID to render calendar (if not provided, one will be generated)
});
Option | Type | Default | Description |
---|---|---|---|
mode |
string | 'day' | Mode of operation: 'day' or 'month' |
displayType |
string | 'tabs' | Display type: 'tabs', 'day', or 'month' |
multipleDays |
boolean | false | Allow multiple date selection |
multipleMonths |
boolean | false | Allow multiple month selection |
maxMonthSelection |
number | null | Maximum number of months that can be selected (when multipleMonths is true) |
startDate |
Date | null | Initial selected date |
minDate |
Date | null | Minimum selectable date |
maxDate |
Date | null | Maximum selectable date |
futureSaturdaysOnly |
boolean | false | Option for day mode to only enable Saturdays in the future |
disabledDaysOfWeek |
array | [] | Array of days of week to disable (0-6, where 0 is Sunday, 6 is Saturday) |
disabledDates |
array | [] | Array of specific dates to disable (Date objects or date strings in various formats) |
startWeekOnMonday |
boolean | false | Option to start the week on Monday instead of Sunday |
monthNames |
array | ['January', ...] | Array of month names |
dayNames |
array | ['Sun', ...] | Array of day names |
cookieName |
string | 'datepickerTabsMode' | Cookie name for mode persistence |
backwardsYearsOffset |
number | 5 | How many year offset render backwards in years selectbox. If now 2025 it will render from 2020 |
forwardsYearsOffset |
number | 5 | How many year offset render forwards in years selectbox. If now 2025 it will render till 2030 |
dateFormat |
string | 'DD MMM YYYY' | Format for displaying dates |
monthFormat |
string | 'MMM YYYY' | Format for displaying months |
position |
string | 'bottom' | Position of the picker: 'bottom' or 'top' |
zIndex |
number | 9999 | z-index for the picker container |
onDateChange |
function | null | Callback function when date(s) change |
containerId |
string | '' | Custom container ID to render calendar (if not provided, one will be generated) |
The date picker supports the following tokens for date formatting:
DD
: Day of month with leading zero (01-31)D
: Day of month without leading zero (1-31)MMM
: Month name short (Jan, Feb, etc.)MMMM
: Month name full (January, February, etc.)MM
: Month number with leading zero (01-12)M
: Month number without leading zero (1-12)YYYY
: Full year (2023)YY
: Short year (23)
DD MMM YYYY = 01 Jan 2025
DD/MM/YYYY = 01/01/2025
MM/DD/YYYY = 01/01/2025
YYYY-MM-DD = 2025-01-01
Sets the selected date(s).
// Set a single date
picker.setDate(new Date());
// Set multiple dates
picker.setMultipleDays(true);
picker.setDate([new Date(2023, 0, 1), new Date(2023, 1, 1)]);
// Clear selection
picker.setDate(null);
Gets the currently selected date(s).
const selectedDate = picker.getDate();
Sets the picker mode ('day' or 'month'). Basically which tab to show active.
picker.setMode('month');
Get the current tab mode show (which tab is active).
picker.getMode();
Sets the display type ('tabs', 'day', or 'month'). When day or month - no tabs are shown. Only one of the two tabs mode.
picker.setDisplayType('day');
Enables or disables multiple days selection.
picker.setMultipleDays(true);
Enables or disables multiple months selection.
picker.setMultipleMonths(true);
Sets the date format for day mode.
picker.setDateFormat('YYYY-MM-DD');
Sets the date format for month mode.
picker.setMonthFormat('MM/YYYY');
Sets the minimum selectable date.
picker.setMinDate(new Date(2023, 0, 1));
Sets the maximum selectable date.
picker.setMaxDate(new Date(2024, 11, 31));
Sets the maximum number of months that can be selected when multiple selection is enabled.
picker.setMaxMonthSelection(3);
Shows the date picker.
picker.show();
Hides the date picker.
picker.hide();
Destroys the date picker instance and cleans up resources.
picker.destroy();
The date picker fires the following custom events:
datepickerShow
: Fired when the date picker is showndatepickerHide
: Fired when the date picker is hiddendatepickerModeChange
: Fired when the mode is changeddatepickerApply
: Fired when dates are applieddatepickerClear
: Fired when dates are cleared
You can listen for these events on the element where the date picker is initialized:
document.getElementById('date-input').addEventListener('datepickerApply', function(e) {
console.log('Applied date(s):', e.detail);
});
const datePicker = new DatepickerTabs('#date-input');
const monthPicker = new DatepickerTabs('#month-input', {
mode: 'month',
multipleMonths: true,
maxMonthSelection: 3
});
const constrainedPicker = new DatepickerTabs('#date-input', {
minDate: new Date(2023, 0, 1),
maxDate: new Date(2023, 11, 31),
dateFormat: 'YYYY-MM-DD'
});
const monthOnlyPicker = new DatepickerTabs('#month-only-picker', {
mode: 'month',
displayType: 'month',
multipleMonths: false,
monthFormat: 'MMM YYYY',
});
const saturdayPicker = new DatepickerTabs('#event-date', {
futureSaturdaysOnly: true,
dateFormat: 'DD/MM/YYYY'
});
const buttomDatePicker = new DatepickerTabs('#bottom-date-input', {
mode: 'both',
onDateChange: function(date) {
console.log('Selected date:', date);
}
});
You can now disable specific days of the week (e.g., disable weekends):
const picker = new DatepickerTabs('#date-input', {
disabledDaysOfWeek: [0, 6] // Disable Sunday (0) and Saturday (6)
});
Disable holidays, special events, or any other specific dates using either Date objects or date strings:
const picker = new DatepickerTabs('#date-input', {
disabledDates: [
// Using Date objects
new Date(2025, 11, 25), // Christmas 2025
// Using date strings (supports multiple formats)
'01/01/2025', // New Year's Day (DD/MM/YYYY)
'2025-07-04', // Independence Day (YYYY-MM-DD)
'11/27/2025' // Thanksgiving (MM/DD/YYYY)
],
dateFormat: 'DD/MM/YYYY' // Format for display and primary string parsing
});
The plugin will try to parse string dates using:
- The specified
dateFormat
option first - Common formats as fallback (DD/MM/YYYY, MM/DD/YYYY, YYYY-MM-DD, DD-MM-YYYY)
Change the calendar to start weeks on Monday instead of Sunday:
const picker = new DatepickerTabs('#date-input', {
startWeekOnMonday: true
});
Create a business day picker that only allows weekdays and excludes holidays:
const businessPicker = new DatepickerTabs('#business-date', {
startWeekOnMonday: true,
disabledDaysOfWeek: [0, 6], // Disable weekends
disabledDates: [
'2025-12-25', // Christmas (YYYY-MM-DD format)
'2025-12-26', // Boxing Day
'2025-01-01' // New Year's Day
],
minDate: new Date(), // Disable past dates
dateFormat: 'YYYY-MM-DD'
});
The date picker works in all modern browsers (Chrome, Firefox, Safari, Edge).
The project uses SCSS for styling and Gulp to compile it to CSS. Here's how to rebuild the styles when you make changes:
- Make sure all dependencies are installed:
npm install --save-dev gulp gulp-sass sass gulp-postcss autoprefixer cssnano gulp-sourcemaps gulp-rename
- Ensure your SCSS files are in the correct location:
src/assets/scss/datepicker-tabs.scss
To compile your SCSS files to CSS once:
npx gulp build
OR:
npm run build
This will:
- Compile your SCSS to CSS
- Add vendor prefixes with Autoprefixer
- Create a minified version (custom-datepicker.min.css)
- Generate sourcemaps for debugging
The compiled CSS files will be in src/assets/css/
.
To automatically rebuild styles whenever you make changes to your SCSS files:
npx gulp
Here's the text starting from "This starts the watch task that will:": This starts the watch task that will:
- Monitor your SCSS files for changes
- Automatically recompile when changes are detected
- Generate both regular and minified CSS files
If you encounter build errors:
- Check for SCSS syntax errors in your files
- Ensure all required dependencies are installed
- Verify the paths in gulpfile.js match your project structure
Common errors:
'compileStyles' errored after X ms
: Look for syntax errors in your SCSSCannot find module 'X'
: You need to install the missing dependency
Please note that all styles are inside the .custom-datepickertabs-container selector So best practice to override some style is to make selector inside the .custom-datepickertabs-container. For example you want to hide the clear and apply buttons, because you are using the single selection mode:
.custom-datepickertabs-container .datepicker-footer {
visibility: hidden;
display: none;
}
To ensure your application responds immediately to user interactions, use the onDateChange callback. This function is executed every time a new date is selected or the selection changes. A common best practice is to update other parts of your user interface or trigger backend operations based on the new date. For example:
const picker = new DatepickerTabs('#date-input', {
onDateChange: function(date) {
console.log('User selected:', date);
// Update the UI with the new date
document.getElementById('date-display').textContent = formatDate(date);
}
});
function formatDate(date) {
const d = new Date(date);
// Return date in a simple DD/MM/YYYY format; adjust as needed
return `${d.getDate()}/${d.getMonth() + 1}/${d.getFullYear()}`;
}
You can test function of parsing of date with prepared test-cases directly if you open html file. in Textarea the fiunction body code - so you can play with or replace with yours. It tests the different format parsing. You can add/edit/replace test cases directly in html code.
This guide explains how to set up Jest to test your DatepickerTabs component directly, without using a stub implementation.
- First, install the necessary dependencies:
npm install --save-dev jest jest-environment-jsdom babel-jest @babel/core @babel/preset-env
- Create the required configuration files or rewrite them if needed:
babel.config.js
- For transpiling modern JavaScriptjest.setup.js
- For setting up the test environment__tests__/direct-import.test.js
- The actual tests
- Update your
package.json
with test scripts if not updated:
"scripts": {
"build": "gulp build",
"dev": "gulp",
"test": "jest",
"prepare": "npm run build"
},
"jest": {
"testEnvironment": "jsdom",
"setupFiles": ["./jest.setup.js"],
"testMatch": [
"**/__tests__/**/*.test.js"
]
}
datepicker-tabs/
├── __tests__/
│ ├── direct-import.test.js
├── babel.config.js
├── jest.setup.js
└── package.json
-
direct-import.test.js: This file reads and loads your original DatepickerTabs code, and run test cases.
-
jest.setup.js: This file sets up the browser-like environment needed by DatepickerTabs, including mocking DOM APIs and global objects.
To run the test suite:
npm test
The test suite is organized as follows:
__tests__/
- Contains all test filesdirect-import.test.js
- Tests directly importing the DatepickerTabs class
The library includes comprehensive tests for the date parsing functionality, covering various formats and edge cases:
// Example test for date parsing
test('should parse a date in DD/MM/YYYY format', () => {
const date = datepicker.parseDate('25/05/2025', 'DD/MM/YYYY');
expect(date instanceof Date).toBe(true);
expect(date.getDate()).toBe(25);
expect(date.getMonth()).toBe(4); // May is month 4 (zero-based)
expect(date.getFullYear()).toBe(2025);
});
We test multiple date formats:
- DD/MM/YYYY
- MM/DD/YYYY
- YYYY-MM-DD
- DD MMM YYYY
- DD MMMM YYYY
- MMM YYYY
- MMMM YYYY
And various edge cases like:
- Empty strings
- Invalid dates
- Invalid formats
- Custom separators
When adding new functionality, please include corresponding tests. Follow these guidelines:
- Create test files in the
__tests__
directory - Use descriptive test names that clearly explain what's being tested
- Test both success cases and edge/failure cases
- Use parametrized tests where appropriate for multiple similar test cases
Example of adding a parametrized test:
const testCases = [
{
name: "DD/MM/YYYY - standard date",
input: "04/07/2025",
format: "DD/MM/YYYY",
expected: new Date(2025, 6, 4) // July 4th, 2025
},
// Add more test cases...
];
test.each(testCases)('$name', ({ input, format, expected }) => {
const result = datepicker.parseDate(input, format);
if (expected === null) {
expect(result).toBeNull();
} else {
expect(result instanceof Date).toBe(true);
expect(result.getDate()).toBe(expected.getDate());
expect(result.getMonth()).toBe(expected.getMonth());
expect(result.getFullYear()).toBe(expected.getFullYear());
}
});
To extend the test suite, simply add more test cases in direct-import.test.js
. For example:
test('should handle special case X', () => {
const picker = new DatepickerTabs('#date-input');
// Your test logic
expect(result).toEqual(expectedValue);
});
-
"Cannot find module" errors: Ensure your file paths are correct.
-
DOM-related errors: Check
jest.setup.js
to make sure necessary DOM APIs are properly mocked. -
TypeError: X is not a function: The DatepickerTabs class might be using browser APIs that need to be mocked.
-
"DatepickerTabs is not a constructor": Verify that
setup-datepicker.js
is correctly loading and exporting the class.
- Add language pre-built support with i18n
Add start of the week with monday optionDoneAdd disable dates arrayDoneTest with jestDone