Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(datetimepicker): resolve issue with active cell highlighting #381

Closed
wants to merge 8 commits into from
64 changes: 33 additions & 31 deletions projects/extensions/datetimepicker/calendar-body.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,41 @@
<tr aria-hidden="true">
<td class="mtx-calendar-body-label" [attr.colspan]="numCols">{{ label }}</td>
</tr>
}

<!-- Create the first row separately so we can include a special spacer cell. -->
@for (row of rows; track row; let rowIndex = $index) {
}
<!-- Create the first row separately so we can include a special spacer cell. -->
@for (row of rows; track rowIndex; let rowIndex = $index) {
<tr role="row">
<!--
We mark this cell as aria-hidden so it doesn't get read out as one of the days in the week.
-->
We mark this cell as aria-hidden so it doesn't get read out as one of the days in the week.
-->
@if (rowIndex === 0 && _firstRowOffset) {
<td
class="mtx-calendar-body-label" [attr.colspan]="_firstRowOffset" aria-hidden="true">
{{ _firstRowOffset >= labelMinRequiredCells ? label : '' }}
</td>
}
@for (item of row; track item; let colIndex = $index) {
<td
role="gridcell"
class="mtx-calendar-body-cell"
[class.mtx-calendar-body-active]="_isActiveCell(rowIndex, colIndex)"
[class.mtx-calendar-body-disabled]="!item.enabled"
[tabindex]="_isActiveCell(rowIndex, colIndex) ? 0 : -1"
[attr.data-mat-row]="rowIndex"
[attr.data-mat-col]="colIndex"
[attr.aria-label]="item.ariaLabel"
[attr.aria-disabled]="!item.enabled || null"
(click)="_cellClicked(item)">
<div class="mtx-calendar-body-cell-content"
[class.mtx-calendar-body-selected]="selectedValue === item.value"
[class.mtx-calendar-body-today]="todayValue === item.value"
[attr.aria-selected]="selectedValue === item.value">
{{ item.displayValue }}
</div>
</td>
<td class="mtx-calendar-body-label" [attr.colspan]="_firstRowOffset" aria-hidden="true">
{{ _firstRowOffset >= labelMinRequiredCells ? label : '' }}
</td>
} @for (item of row; track item.displayValue; let colIndex = $index) {
<td
role="gridcell"
class="mtx-calendar-body-cell"
[class.mtx-calendar-body-active]="_isActiveCell(rowIndex, colIndex)"
[class.mtx-calendar-body-disabled]="!item.enabled"
[tabindex]="_isActiveCell(rowIndex, colIndex) ? 0 : -1"
[attr.data-mat-row]="rowIndex"
[attr.data-mat-col]="colIndex"
[attr.aria-label]="item.ariaLabel"
[attr.aria-disabled]="!item.enabled || null"
(click)="_cellClicked(item)"
>
<div
class="mtx-calendar-body-cell-content"
[class.mtx-calendar-body-selected]="selectedValue === item.value"
[class.mtx-calendar-body-today]="todayValue === item.value"
[attr.aria-selected]="selectedValue === item.value"
>
{{ item.displayValue }}
</div>
</td>
}
</tr>
}
}

Choose a reason for hiding this comment

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

new empty line

12 changes: 7 additions & 5 deletions projects/extensions/datetimepicker/month-view.html
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
<table class="mtx-calendar-table" role="grid">
<thead class="mtx-calendar-table-header">
<tr>
@for (day of _weekdays; track day) {
<th [attr.aria-label]="day.long">{{day.narrow}}</th>
@for (day of _weekdays; track day.long) {
<th [attr.aria-label]="day.long">{{day.narrow}}</th>
}
</tr>
</thead>
<tbody mtx-calendar-body
<tbody
mtx-calendar-body
(@slideCalendar.done)="_calendarStateDone()"
[@slideCalendar]="_calendarState"
[rows]="_weeks"
[todayValue]="_todayDate!"
[activeCell]="_adapter.getDate(activeDate) - 1"
[activeCell]="_getActiveCell()"
Copy link
Member

Choose a reason for hiding this comment

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

The active cell logic has no issue, we just should improve the a11y.
https://github.com/angular/components/blob/main/src/material/datepicker/month-view.html#L25

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I figured it out! Material has implemented a11y very effectively.

[selectedValue]="_selectedDate!"
(selectedValueChange)="_dateSelected($event)"></tbody>
(selectedValueChange)="_dateSelected($event)"
></tbody>
</table>
32 changes: 32 additions & 0 deletions projects/extensions/datetimepicker/month-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export class MtxMonthView<D> implements AfterContentInit {
this._weekdays = weekdays.slice(firstDayOfWeek).concat(weekdays.slice(0, firstDayOfWeek));

this._activeDate = this._adapter.today();
this._selected = this._adapter.today();
}

private _activeDate: D;
Expand Down Expand Up @@ -145,6 +146,7 @@ export class MtxMonthView<D> implements AfterContentInit {
);
this.selectedChange.emit(dateObject);
this._activeDate = dateObject;
this.selected = dateObject;

if (this.type === 'date') {
this._userSelection.emit();
Expand Down Expand Up @@ -214,4 +216,34 @@ export class MtxMonthView<D> implements AfterContentInit {
private calendarState(direction: string): void {
this._calendarState = direction;
}

/**
* Gets the index of the cell that should be highlighted in the month view.
* Used to highlight either the selected date or today's date in the calendar.
*
* Highlighting priority:
* 1. Currently selected date (if it's in the displayed month)
* 2. Today's date (if it's in the displayed month)
* 3. No highlight (-1) if neither date is in the current month
*
* @returns Zero-based index of the cell to highlight, or -1 if no cell should be highlighted
*/
protected _getActiveCell(): number {
if (!this.activeDate || !this._adapter) {
return -1;
}

// Check if selected date is in current month - highlight it if so
if (this.selected && this._adapter.sameMonthAndYear(this.activeDate, this.selected)) {
return Math.max(0, this._adapter.getDate(this.selected) - 1);
}

// Otherwise highlight today's date if it's in the current month and not selected
const today = this._adapter.today();
if (!this.selected && this._adapter.sameMonthAndYear(this.activeDate, today)) {
return Math.max(0, this._adapter.getDate(today) - 1);
}

return -1;
}
}