Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7cc70a1
Add isDateAvailable property
dethell Jan 5, 2023
6a49802
Further changes to allow disabling arbitrary dates
dethell Jan 6, 2023
7df3432
Starting to add tests for isDateAvailable changes
dethell Jan 9, 2023
e58f796
Add validation and disabled tests
dethell Jan 17, 2023
0c119cf
refactor: Refactoring per review comments
dethell Sep 21, 2023
848efbe
feat(date-picker): Add disabled function to demo
dethell Sep 26, 2023
41865b2
feat(date-picker): update month-calendar tests
dethell Oct 5, 2023
8c687f2
feat(date-picker): Update datepicker playground to ensure disabled fu…
dethell Oct 5, 2023
2474fe7
feat(date-picker): month-calendar should not use isDateDisabled funct…
dethell Oct 5, 2023
efadb48
fix(date-picker): change per review.
dethell Oct 6, 2023
077d79f
feat(date-picker): Update isDateDisabled contract to use DatePickerDa…
dethell Oct 12, 2023
f9b077e
feat(date-picker): Update date-picker html demo to provide sample isD…
dethell Oct 12, 2023
47cd2bb
feat(date-picker): Update keyboard logic for disabled dates
dethell Oct 16, 2023
93c8e1d
Merge remote-tracking branch 'upstream/main'
dethell Oct 18, 2023
e67599d
feat(date-picker): Update date-picker demo
dethell Oct 27, 2023
8bc4cfd
fix(date-picker): Fix bug in overlay mixin _selectDate function
dethell Oct 27, 2023
a963bd4
fix(date-picker): update disabled dates test to use DatePickerDate type
dethell Oct 27, 2023
46a33d4
feat(date-picker): Add tests for keyboard navigation
dethell Oct 27, 2023
1c103f8
fix(date-picker): Update getDayAriaDisabled logic to account for cust…
dethell Nov 8, 2023
459a783
fix(date-picker): update month-calendar tests for disabled date function
dethell Nov 8, 2023
3b2446d
fix(date-picker): fix lint issues
dethell Nov 10, 2023
07e8687
chore(date-picker): move keyboard-related disabled date tests to keyb…
dethell Nov 14, 2023
2268b6a
Merge remote-tracking branch 'upstream/main'
dethell Nov 14, 2023
59750ee
fix(date-picker): update keyboard validation tests
dethell Nov 15, 2023
1dd26c8
handle async property update in a Lit test
tomivirkki Nov 16, 2023
1010d3c
fix(date-picker): per review - use dateAllowed helper function instea…
dethell Nov 16, 2023
9ab7e8a
test: align tests with existing tests
tomivirkki Nov 17, 2023
4e93815
feat(date-picker): Update keyboard test for disabled dates to test ENTER
dethell Nov 21, 2023
2143f0e
Merge branch 'main' into dethell-jh
tomivirkki Nov 22, 2023
f0793b9
test: split cases into separate tests
tomivirkki Nov 22, 2023
a1d37c1
test: fix test errors in the console
tomivirkki Nov 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dev/date-picker.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
<script type="module">
import '@vaadin/date-picker';
import '@vaadin/tooltip';
const isDateDisabled = (date) => !!(date?.getDay() === 0);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a sample implementation in the demo app. We can drop this or add some docs as needed to describe what's going on.

const picker = document.querySelector('vaadin-date-picker');
picker.isDateDisabled = isDateDisabled;
</script>
</head>
<body>
Expand Down
2 changes: 1 addition & 1 deletion packages/date-picker/src/vaadin-date-picker-helper.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ declare function dateEquals(date1: Date | null, date2: Date | null): boolean;
*
* @returns True if the date is in the range
*/
declare function dateAllowed(date: Date, min: Date | null, max: Date | null): boolean;
declare function dateAllowed(date: Date, min: Date | null, max: Date | null, isDateDisabled: Function | null): boolean;

/**
* Get closest date from array of dates.
Expand Down
6 changes: 4 additions & 2 deletions packages/date-picker/src/vaadin-date-picker-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ export function dateEquals(date1, date2) {
* @param {!Date} date The date to check
* @param {Date} min Range start
* @param {Date} max Range end
* @param {function(!Date): boolean} isDateDisabled Callback to check if the date is disabled
* @return {boolean} True if the date is in the range
*/
export function dateAllowed(date, min, max) {
return (!min || date >= min) && (!max || date <= max);
export function dateAllowed(date, min, max, isDateDisabled) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the heavy lifting function that does all the work of deciding a date needs to be disabled. So all the components that need to disable a date end up feeding here.

const dateIsDisabled = typeof isDateDisabled === 'function' ? isDateDisabled(date) : false;
return (!min || date >= min) && (!max || date <= max) && !dateIsDisabled;
}

/**
Expand Down
5 changes: 5 additions & 0 deletions packages/date-picker/src/vaadin-date-picker-mixin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,11 @@ export declare class DatePickerMixinClass {
*/
max: string | undefined;

/**
* A function that is used to determine if a date should be disabled.
*/
isDateDisabled: (date: Date) => boolean;

/**
* Opens the dropdown.
*/
Expand Down
22 changes: 16 additions & 6 deletions packages/date-picker/src/vaadin-date-picker-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
extractDateParts,
getAdjustedYear,
getClosestDate,
parseDate,
parseDate
} from './vaadin-date-picker-helper.js';

/**
Expand Down Expand Up @@ -302,6 +302,15 @@ export const DatePickerMixin = (subclass) =>
sync: true,
},

/**
* A function that is used to determine if a date should be disabled.
*
* @type {function(Date): boolean | undefined}
*/
isDateDisabled: {
type: Function,
},

/**
* The earliest date that can be selected. All earlier dates will be disabled.
* @type {Date | undefined}
Expand Down Expand Up @@ -365,7 +374,7 @@ export const DatePickerMixin = (subclass) =>
return [
'_selectedDateChanged(_selectedDate, i18n)',
'_focusedDateChanged(_focusedDate, i18n)',
'__updateOverlayContent(_overlayContent, i18n, label, _minDate, _maxDate, _focusedDate, _selectedDate, showWeekNumbers)',
'__updateOverlayContent(_overlayContent, i18n, label, _minDate, _maxDate, _focusedDate, _selectedDate, showWeekNumbers, isDateDisabled)',
'__updateOverlayContentTheme(_overlayContent, _theme)',
'__updateOverlayContentFullScreen(_overlayContent, _fullscreen)',
];
Expand Down Expand Up @@ -585,7 +594,7 @@ export const DatePickerMixin = (subclass) =>
checkValidity() {
const inputValue = this._inputElementValue;
const inputValid = !inputValue || (!!this._selectedDate && inputValue === this.__formatDate(this._selectedDate));
const minMaxValid = !this._selectedDate || dateAllowed(this._selectedDate, this._minDate, this._maxDate);
const isDateValid = !this._selectedDate || dateAllowed(this._selectedDate, this._minDate, this._maxDate, this.isDateDisabled);

let inputValidity = true;
if (this.inputElement) {
Expand All @@ -597,7 +606,7 @@ export const DatePickerMixin = (subclass) =>
}
}

return inputValid && minMaxValid && inputValidity;
return inputValid && isDateValid && inputValidity;
}

/**
Expand Down Expand Up @@ -806,7 +815,7 @@ export const DatePickerMixin = (subclass) =>

/** @private */
// eslint-disable-next-line max-params
__updateOverlayContent(overlayContent, i18n, label, minDate, maxDate, focusedDate, selectedDate, showWeekNumbers) {
__updateOverlayContent(overlayContent, i18n, label, minDate, maxDate, focusedDate, selectedDate, showWeekNumbers, isDateDisabled) {
if (overlayContent) {
overlayContent.i18n = i18n;
overlayContent.label = label;
Expand All @@ -815,6 +824,7 @@ export const DatePickerMixin = (subclass) =>
overlayContent.focusedDate = focusedDate;
overlayContent.selectedDate = selectedDate;
overlayContent.showWeekNumbers = showWeekNumbers;
overlayContent.isDateDisabled = isDateDisabled;
}
}

Expand Down Expand Up @@ -886,7 +896,7 @@ export const DatePickerMixin = (subclass) =>
const initialPosition =
this._selectedDate || this._overlayContent.initialPosition || parsedInitialPosition || new Date();

return parsedInitialPosition || dateAllowed(initialPosition, this._minDate, this._maxDate)
return parsedInitialPosition || dateAllowed(initialPosition, this._minDate, this._maxDate, this.isDateDisabled)
? initialPosition
: getClosestDate(initialPosition, [this._minDate, this._maxDate]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ export const DatePickerOverlayContentMixin = (superClass) =>
sync: true,
},

/**
* A function that is used to determine if a date should be disabled.
*
* @type {function(Date): boolean | undefined}
*/
isDateDisabled: {
type: Function,
},

/**
* Input label
*/
Expand Down Expand Up @@ -134,9 +143,9 @@ export const DatePickerOverlayContentMixin = (superClass) =>

static get observers() {
return [
'__updateCalendars(calendars, i18n, minDate, maxDate, selectedDate, focusedDate, showWeekNumbers, _ignoreTaps, _theme)',
'__updateCalendars(calendars, i18n, minDate, maxDate, selectedDate, focusedDate, showWeekNumbers, _ignoreTaps, _theme, isDateDisabled)',
'__updateCancelButton(_cancelButton, i18n)',
'__updateTodayButton(_todayButton, i18n, minDate, maxDate)',
'__updateTodayButton(_todayButton, i18n, minDate, maxDate, isDateDisabled)',
'__updateYears(years, selectedDate, _theme)',
];
}
Expand Down Expand Up @@ -303,10 +312,10 @@ export const DatePickerOverlayContentMixin = (superClass) =>
}

/** @private */
__updateTodayButton(todayButton, i18n, minDate, maxDate) {
__updateTodayButton(todayButton, i18n, minDate, maxDate, isDateDisabled) {
if (todayButton) {
todayButton.textContent = i18n && i18n.today;
todayButton.disabled = !this._isTodayAllowed(minDate, maxDate);
todayButton.disabled = !this._isTodayAllowed(minDate, maxDate, isDateDisabled);
}
}

Expand All @@ -321,12 +330,14 @@ export const DatePickerOverlayContentMixin = (superClass) =>
showWeekNumbers,
ignoreTaps,
theme,
isDateDisabled,
) {
if (calendars && calendars.length) {
calendars.forEach((calendar) => {
calendar.i18n = i18n;
calendar.minDate = minDate;
calendar.maxDate = maxDate;
calendar.isDateDisabled = isDateDisabled;
calendar.focusedDate = focusedDate;
calendar.selectedDate = selectedDate;
calendar.showWeekNumbers = showWeekNumbers;
Expand Down Expand Up @@ -1009,18 +1020,18 @@ export const DatePickerOverlayContentMixin = (superClass) =>
}

/** @private */
_dateAllowed(date, min = this.minDate, max = this.maxDate) {
return (!min || date >= min) && (!max || date <= max);
_dateAllowed(date, min = this.minDate, max = this.maxDate, isDateDisabled = this.isDateDisabled) {
return (!min || date >= min) && (!max || date <= max) && (!isDateDisabled || !isDateDisabled(date));
}

/** @private */
_isTodayAllowed(min, max) {
_isTodayAllowed(min, max, isDateDisabled) {
const today = new Date();
const todayMidnight = new Date(0, 0);
todayMidnight.setFullYear(today.getFullYear());
todayMidnight.setMonth(today.getMonth());
todayMidnight.setDate(today.getDate());
return this._dateAllowed(todayMidnight, min, max);
return this._dateAllowed(todayMidnight, min, max, isDateDisabled);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/date-picker/src/vaadin-lit-month-calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class MonthCalendar extends MonthCalendarMixin(ThemableMixin(PolylitMixin(LitEle
${week.map((date) => {
const isFocused = dateEquals(date, this.focusedDate);
const isSelected = dateEquals(date, this.selectedDate);
const isDisabled = !dateAllowed(date, this.minDate, this.maxDate);
const isDisabled = !dateAllowed(date, this.minDate, this.maxDate, this.isDateDisabled);

const parts = [
'date',
Expand Down Expand Up @@ -102,7 +102,7 @@ class MonthCalendar extends MonthCalendarMixin(ThemableMixin(PolylitMixin(LitEle
this._weeks = this._getWeeks(this._days);
}

if (props.has('month') || props.has('minDate') || props.has('maxDate')) {
if (props.has('month') || props.has('minDate') || props.has('maxDate') || props.has('isDateDisabled')) {
this.disabled = this._isDisabled(this.month, this.minDate, this.maxDate);
}

Expand Down
11 changes: 11 additions & 0 deletions packages/date-picker/src/vaadin-month-calendar-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ export const MonthCalendarMixin = (superClass) =>
sync: true,
},

/**
* A function to be used to determine whether the user can select a given date.
* Receives a `Date` object of the date to be selected and should return a
* boolean.
* @type {Function | undefined}
*/
isDateDisabled: {
type: Function,
value: () => false,
},

disabled: {
type: Boolean,
reflectToAttribute: true,
Expand Down
20 changes: 10 additions & 10 deletions packages/date-picker/src/vaadin-month-calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ class MonthCalendar extends MonthCalendarMixin(ThemableMixin(PolymerElement)) {
<template is="dom-repeat" items="[[week]]">
<td
role="gridcell"
part$="[[__getDatePart(item, focusedDate, selectedDate, minDate, maxDate)]]"
part$="[[__getDatePart(item, focusedDate, selectedDate, minDate, maxDate, isDateDisabled)]]"
date="[[item]]"
tabindex$="[[__getDayTabindex(item, focusedDate)]]"
disabled$="[[__isDayDisabled(item, minDate, maxDate)]]"
disabled$="[[__isDayDisabled(item, minDate, maxDate, isDateDisabled)]]"
aria-selected$="[[__getDayAriaSelected(item, selectedDate)]]"
aria-disabled$="[[__getDayAriaDisabled(item, minDate, maxDate)]]"
aria-disabled$="[[__getDayAriaDisabled(item, minDate, maxDate, isDateDisabled)]]"
aria-label$="[[__getDayAriaLabel(item)]]"
>[[_getDate(item)]]</td
>
Expand All @@ -76,7 +76,7 @@ class MonthCalendar extends MonthCalendarMixin(ThemableMixin(PolymerElement)) {
/** @protected */
_days: {
type: Array,
computed: '_getDays(month, i18n, minDate, maxDate)',
computed: '_getDays(month, i18n, minDate, maxDate, isDateDisabled)',
},

/** @protected */
Expand Down Expand Up @@ -107,10 +107,10 @@ class MonthCalendar extends MonthCalendarMixin(ThemableMixin(PolymerElement)) {
}

/** @private */
__getDatePart(date, focusedDate, selectedDate, minDate, maxDate) {
__getDatePart(date, focusedDate, selectedDate, minDate, maxDate, isDateDisabled) {
const result = ['date'];

if (this.__isDayDisabled(date, minDate, maxDate)) {
if (this.__isDayDisabled(date, minDate, maxDate, isDateDisabled)) {
result.push('disabled');
}

Expand Down Expand Up @@ -147,17 +147,17 @@ class MonthCalendar extends MonthCalendarMixin(ThemableMixin(PolymerElement)) {
}

/** @private */
__isDayDisabled(date, minDate, maxDate) {
return !dateAllowed(date, minDate, maxDate);
__isDayDisabled(date, minDate, maxDate, isDateDisabled) {
return !dateAllowed(date, minDate, maxDate, isDateDisabled);
}

/** @private */
__getDayAriaDisabled(date, min, max) {
__getDayAriaDisabled(date, min, max, isDateDisabled) {
if (date === undefined || min === undefined || max === undefined) {
return;
}

if (this.__isDayDisabled(date, min, max)) {
if (this.__isDayDisabled(date, min, max, isDateDisabled)) {
return 'true';
}
}
Expand Down
Loading