Skip to content

Commit

Permalink
refactor: update month calendar to use table (#3355)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan committed Jan 31, 2022
1 parent fac49f2 commit 59fe71e
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 203 deletions.
215 changes: 129 additions & 86 deletions packages/date-picker/src/vaadin-month-calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,23 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
display: block;
}
[part='weekdays'],
#days {
#monthGrid {
display: block;
}
#monthGrid thead,
#monthGrid tbody {
display: block;
width: 100%;
}
[part='weekdays'] {
display: flex;
flex-wrap: wrap;
flex-grow: 1;
}
#days-container,
#weekdays-container {
#days-container tr,
#weekdays-container tr {
display: flex;
}
Expand All @@ -40,15 +48,19 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
flex-shrink: 0;
}
[part='week-number'][hidden],
[part='week-numbers'][hidden],
[part='weekday'][hidden] {
display: none;
}
[part='weekday'],
[part='date'] {
display: block;
/* Would use calc(100% / 7) but it doesn't work nice on IE */
width: 14.285714286%;
padding: 0;
font-weight: normal;
}
[part='weekday']:empty,
Expand All @@ -58,42 +70,58 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
}
</style>
<div part="month-header" role="heading">[[_getTitle(month, i18n.monthNames)]]</div>
<div id="monthGrid" on-touchend="_preventDefault" on-touchstart="_onMonthGridTouchStart">
<div id="weekdays-container">
<div hidden$="[[!_showWeekSeparator(showWeekNumbers, i18n.firstDayOfWeek)]]" part="weekday"></div>
<div part="weekdays">
<div part="month-header" id="month-header" aria-hidden="true">[[_getTitle(month, i18n.monthNames)]]</div>
<table
id="monthGrid"
role="grid"
aria-labelledby="month-header"
on-touchend="_preventDefault"
on-touchstart="_onMonthGridTouchStart"
>
<thead id="weekdays-container">
<tr role="row" part="weekdays">
<th
part="weekday"
aria-hidden="true"
hidden$="[[!_showWeekSeparator(showWeekNumbers, i18n.firstDayOfWeek)]]"
></th>
<template
is="dom-repeat"
items="[[_getWeekDayNames(i18n.weekdays, i18n.weekdaysShort, showWeekNumbers, i18n.firstDayOfWeek)]]"
>
<div part="weekday" role="heading" aria-label$="[[item.weekDay]]">[[item.weekDayShort]]</div>
<th role="columnheader" part="weekday" scope="col" abbr$="[[item.weekDay]]">[[item.weekDayShort]]</th>
</template>
</div>
</div>
<div id="days-container">
<div part="week-numbers" hidden$="[[!_showWeekSeparator(showWeekNumbers, i18n.firstDayOfWeek)]]">
<template is="dom-repeat" items="[[_getWeekNumbers(_days)]]">
<div part="week-number" role="heading" aria-label$="[[i18n.week]] [[item]]">[[item]]</div>
</template>
</div>
<div id="days">
<template is="dom-repeat" items="[[_days]]">
<!-- prettier-ignore -->
<div
part="date"
today$="[[_isToday(item)]]"
selected$="[[_dateEquals(item, selectedDate)]]"
focused$="[[_dateEquals(item, focusedDate)]]"
date="[[item]]"
disabled$="[[!_dateAllowed(item, minDate, maxDate)]]"
role$="[[_getRole(item)]]"
aria-label$="[[_getAriaLabel(item)]]"
aria-disabled$="[[_getAriaDisabled(item, minDate, maxDate)]]">[[_getDate(item)]]</div>
</template>
</div>
</div>
</div>
</tr>
</thead>
<tbody id="days-container">
<template is="dom-repeat" items="[[_weeks]]" as="week">
<tr role="row">
<td
part="week-number"
aria-hidden="true"
hidden$="[[!_showWeekSeparator(showWeekNumbers, i18n.firstDayOfWeek)]]"
>
[[__getWeekNumber(week)]]
</td>
<template is="dom-repeat" items="[[week]]">
<td
role="gridcell"
part="date"
date="[[item]]"
today$="[[_isToday(item)]]"
focused$="[[__isDayFocused(item, focusedDate)]]"
tabindex$="[[__getDayTabindex(item, focusedDate)]]"
selected$="[[__isDaySelected(item, selectedDate)]]"
disabled$="[[__isDayDisabled(item, minDate, maxDate)]]"
aria-selected$="[[__getDayAriaSelected(item, selectedDate)]]"
aria-disabled$="[[__getDayAriaDisabled(item, minDate, maxDate)]]"
>[[_getDate(item)]]</td
>
</template>
</tr>
</template>
</tbody>
</table>
`;
}

Expand Down Expand Up @@ -162,6 +190,11 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
computed: '_getDays(month, i18n.firstDayOfWeek, minDate, maxDate)'
},

_weeks: {
type: Array,
computed: '_getWeeks(_days)'
},

disabled: {
type: Boolean,
reflectToAttribute: true,
Expand All @@ -171,7 +204,10 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
}

static get observers() {
return ['_showWeekNumbersChanged(showWeekNumbers, i18n.firstDayOfWeek)'];
return [
'_showWeekNumbersChanged(showWeekNumbers, i18n.firstDayOfWeek)',
'__focusedDateChanged(focusedDate, _days)'
];
}

/** @protected */
Expand All @@ -180,14 +216,6 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
addListener(this.$.monthGrid, 'tap', this._handleTap.bind(this));
}

_dateEquals(date1, date2) {
return dateEquals(date1, date2);
}

_dateAllowed(date, min, max) {
return dateAllowed(date, min, max);
}

/* Returns true if all the dates in the month are out of the allowed range */
_isDisabled(month, minDate, maxDate) {
// First day of the month
Expand All @@ -212,7 +240,7 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
return false;
}

return !this._dateAllowed(firstDate, minDate, maxDate) && !this._dateAllowed(lastDate, minDate, maxDate);
return !dateAllowed(firstDate, minDate, maxDate) && !dateAllowed(lastDate, minDate, maxDate);
}

_getTitle(month, monthNames) {
Expand Down Expand Up @@ -260,6 +288,14 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
return weekDayNames;
}

__focusedDateChanged(focusedDate, days) {
if (days.some((date) => dateEquals(date, focusedDate))) {
this.removeAttribute('aria-hidden');
} else {
this.setAttribute('aria-hidden', 'true');
}
}

_getDate(date) {
return date ? date.getDate() : '';
}
Expand All @@ -278,7 +314,7 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
}

_isToday(date) {
return this._dateEquals(new Date(), date);
return dateEquals(new Date(), date);
}

_getDays(month, firstDayOfWeek) {
Expand Down Expand Up @@ -308,25 +344,14 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
return days;
}

_getWeekNumber(date, days) {
if (date === undefined || days === undefined) {
return;
}

if (!date) {
// Get the first non-null date from the days array.
date = days.reduce((acc, d) => {
return !acc && d ? d : acc;
});
}

return getISOWeekNumber(date);
}

_getWeekNumbers(dates) {
return dates
.map((date) => this._getWeekNumber(date, dates))
.filter((week, index, arr) => arr.indexOf(week) === index);
_getWeeks(days) {
return days.reduce((acc, day, i) => {
if (i % 7 === 0) {
acc.push([]);
}
acc[acc.length - 1].push(day);
return acc;
}, []);
}

_handleTap(e) {
Expand All @@ -340,36 +365,54 @@ class MonthCalendar extends ThemableMixin(PolymerElement) {
e.preventDefault();
}

_getRole(date) {
return date ? 'button' : 'presentation';
__getWeekNumber(days) {
const date = days.reduce((acc, d) => {
return !acc && d ? d : acc;
});

return getISOWeekNumber(date);
}

_getAriaLabel(date) {
if (!date) {
return '';
}
__isDayFocused(date, focusedDate) {
return dateEquals(date, focusedDate);
}

__isDaySelected(date, selectedDate) {
return dateEquals(date, selectedDate);
}

var ariaLabel =
this._getDate(date) +
' ' +
this.i18n.monthNames[date.getMonth()] +
' ' +
date.getFullYear() +
', ' +
this.i18n.weekdays[date.getDay()];

if (this._isToday(date)) {
ariaLabel += ', ' + this.i18n.today;
__getDayAriaSelected(date, selectedDate) {
if (this.__isDaySelected(date, selectedDate)) {
return 'true';
}
}

return ariaLabel;
__isDayDisabled(date, minDate, maxDate) {
return !dateAllowed(date, minDate, maxDate);
}

_getAriaDisabled(date, min, max) {
__getDayAriaDisabled(date, min, max) {
if (date === undefined || min === undefined || max === undefined) {
return;
}
return this._dateAllowed(date, min, max) ? 'false' : 'true';

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

__getDayTabindex(date, focusedDate) {
if (this.__isDayFocused(date, focusedDate)) {
return '0';
}

return '-1';
}

__getWeekNumbers(dates) {
return dates
.map((date) => this.__getWeekNumber(date, dates))
.filter((week, index, arr) => arr.indexOf(week) === index);
}
}

Expand Down
10 changes: 0 additions & 10 deletions packages/date-picker/test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,17 +326,7 @@ describe('basic features', () => {
it('should notify i18n mutation to children', () => {
const monthCalendar = overlayContent.$.monthScroller.querySelector('vaadin-month-calendar');
const weekdays = monthCalendar.$.monthGrid.querySelectorAll('[part="weekday"]:not(:empty)');
const weekdayLabels = Array.prototype.map.call(weekdays, (weekday) => weekday.getAttribute('aria-label'));
const weekdayTitles = Array.prototype.map.call(weekdays, (weekday) => weekday.textContent);
expect(weekdayLabels).to.eql([
'maanantai',
'tiistai',
'keskiviikko',
'torstai',
'perjantai',
'lauantai',
'sunnuntai'
]);
expect(weekdayTitles).to.eql(['ma', 'ti', 'ke', 'to', 'pe', 'la', 'su']);
});

Expand Down
Loading

0 comments on commit 59fe71e

Please sign in to comment.