Skip to content

Commit

Permalink
fix(datepicker): not showing single-day comparison ranges (#20102)
Browse files Browse the repository at this point in the history
The logic that determines whether we show something as being a range doesn't allow for the start and end to be on the same date, because it increases the CSS complexity a lot. As a result, we aren't able to render ranges that start and end on the same day. This isn't a problem for the main range, because we style its start and end as selected which looks exactly the same as the start/end of a range, however it breaks down for the comparison range since it doesn't have the same selected styling for its start/end cells.

These changes fix the issue by introducing a special class for the case where the comparison range is for a single day. While it's not ideal, it's preferrable to complicating the CSS for the range styling.

Fixes #20100.
  • Loading branch information
crisbeto committed Aug 12, 2020
1 parent 098be5f commit 0433cd9
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 26 deletions.
63 changes: 42 additions & 21 deletions src/material/datepicker/_datepicker-theme.scss
Expand Up @@ -40,10 +40,18 @@ $mat-calendar-weekday-table-font-size: 11px !default;
}
}

// Utility mixin to target cells that aren't selected. Used to make selector easier to follow.
@mixin _mat-datepicker-unselected-cell {
&:not(.mat-calendar-body-selected):not(.mat-calendar-body-comparison-identical) {
@content;
}
}

@mixin mat-datepicker-color($config-or-theme) {
$config: mat-get-color-config($config-or-theme);
$foreground: map-get($config, foreground);
$background: map-get($config, background);
$disabled-color: mat-color($foreground, disabled-text);

.mat-calendar-arrow {
border-top-color: mat-color($foreground, icon);
Expand Down Expand Up @@ -75,16 +83,23 @@ $mat-calendar-weekday-table-font-size: 11px !default;
border-color: transparent;
}

.mat-calendar-body-disabled > .mat-calendar-body-cell-content:not(.mat-calendar-body-selected),
.mat-calendar-body-disabled > .mat-calendar-body-cell-content {
@include _mat-datepicker-unselected-cell {
color: $disabled-color;
}
}

.mat-form-field-disabled .mat-date-range-input-separator {
color: mat-color($foreground, disabled-text);
color: $disabled-color;
}

.mat-calendar-body-cell:not(.mat-calendar-body-disabled):hover,
.cdk-keyboard-focused .mat-calendar-body-active,
.cdk-program-focused .mat-calendar-body-active {
& > .mat-calendar-body-cell-content:not(.mat-calendar-body-selected) {
background-color: mat-color($background, hover);
& > .mat-calendar-body-cell-content {
@include _mat-datepicker-unselected-cell {
background-color: mat-color($background, hover);
}
}
}

Expand All @@ -102,24 +117,28 @@ $mat-calendar-weekday-table-font-size: 11px !default;
}
}

.mat-calendar-body-today:not(.mat-calendar-body-selected) {
// Note: though it's not text, the border is a hint about the fact that this is today's date,
// so we use the hint color.
border-color: mat-color($foreground, hint-text);
.mat-calendar-body-today {
@include _mat-datepicker-unselected-cell {
// Note: though it's not text, the border is a hint about the fact that this is today's date,
// so we use the hint color.
border-color: mat-color($foreground, hint-text);
}
}

.mat-calendar-body-disabled > .mat-calendar-body-today:not(.mat-calendar-body-selected) {
$color: mat-color($foreground, hint-text);

@if (type-of($color) == color) {
border-color: fade-out($color, $mat-datepicker-today-fade-amount);
}
@else {
// If the color didn't resolve to a color value, but something like a CSS variable, we can't
// fade it out so we fall back to reducing the element opacity. Note that we don't use the
// $mat-datepicker-today-fade-amount, because hint text usually has some opacity applied
// to it already and we don't want them to stack on top of each other.
opacity: 0.5;
.mat-calendar-body-disabled > .mat-calendar-body-today {
@include _mat-datepicker-unselected-cell {
$color: mat-color($foreground, hint-text);

@if (type-of($color) == color) {
border-color: fade-out($color, $mat-datepicker-today-fade-amount);
}
@else {
// If the color didn't resolve to a color value, but something like a CSS variable, we can't
// fade it out so we fall back to reducing the element opacity. Note that we don't use the
// $mat-datepicker-today-fade-amount, because hint text usually has some opacity applied
// to it already and we don't want them to stack on top of each other.
opacity: 0.5;
}
}
}

Expand Down Expand Up @@ -196,6 +215,7 @@ $mat-calendar-weekday-table-font-size: 11px !default;
background: $range-color;
}

.mat-calendar-body-comparison-identical,
.mat-calendar-body-in-comparison-range::before {
background: $comparison-color;
}
Expand All @@ -210,13 +230,14 @@ $mat-calendar-weekday-table-font-size: 11px !default;
background: linear-gradient(to left, $range-color 50%, $comparison-color 50%);
}

.mat-calendar-body-in-range > .mat-calendar-body-comparison-identical,
.mat-calendar-body-in-comparison-range.mat-calendar-body-in-range::after {
background: $overlap-color;
}

.mat-calendar-body-comparison-identical.mat-calendar-body-selected,
.mat-calendar-body-in-comparison-range > .mat-calendar-body-selected {
background: $overlap-selected-color;

}
}

Expand Down
5 changes: 3 additions & 2 deletions src/material/datepicker/calendar-body.html
Expand Up @@ -49,13 +49,14 @@
[class.mat-calendar-body-in-preview]="_isInPreview(item.compareValue)"
[attr.aria-label]="item.ariaLabel"
[attr.aria-disabled]="!item.enabled || null"
[attr.aria-selected]="_isSelected(item)"
[attr.aria-selected]="_isSelected(item.compareValue)"
(click)="_cellClicked(item, $event)"
[style.width]="_cellWidth"
[style.paddingTop]="_cellPadding"
[style.paddingBottom]="_cellPadding">
<div class="mat-calendar-body-cell-content mat-focus-indicator"
[class.mat-calendar-body-selected]="_isSelected(item)"
[class.mat-calendar-body-selected]="_isSelected(item.compareValue)"
[class.mat-calendar-body-comparison-identical]="_isComparisonIdentical(item.compareValue)"
[class.mat-calendar-body-today]="todayValue === item.compareValue">
{{item.displayValue}}
</div>
Expand Down
17 changes: 17 additions & 0 deletions src/material/datepicker/calendar-body.spec.ts
Expand Up @@ -566,6 +566,23 @@ describe('MatCalendarBody', () => {
expect(cells[5].classList).toContain(previewEndClass);
});

it('should mark a cell as being identical to the comparison range', () => {
testComponent.comparisonStart = testComponent.comparisonEnd = 3;
fixture.detectChanges();

const comparisonIdenticalCells: NodeListOf<HTMLElement> =
fixture.nativeElement.querySelectorAll('.mat-calendar-body-comparison-identical');

expect(comparisonIdenticalCells.length).toBe(1);
expect(cells[2].contains(comparisonIdenticalCells[0])).toBe(true);
expect(cells.some(cell => {
const classList = cell.classList;
return classList.contains(startClass) || classList.contains(inRangeClass) ||
classList.contains(endClass) || classList.contains(comparisonStartClass) ||
classList.contains(inComparisonClass) || classList.contains(comparisonEndClass);
})).toBe(false);
});

});

});
Expand Down
20 changes: 18 additions & 2 deletions src/material/datepicker/calendar-body.ts
Expand Up @@ -149,8 +149,8 @@ export class MatCalendarBody implements OnChanges, OnDestroy {
}

/** Returns whether a cell should be marked as selected. */
_isSelected(cell: MatCalendarCell) {
return this.startValue === cell.compareValue || this.endValue === cell.compareValue;
_isSelected(value: number) {
return this.startValue === value || this.endValue === value;
}

ngOnChanges(changes: SimpleChanges) {
Expand Down Expand Up @@ -270,6 +270,22 @@ export class MatCalendarBody implements OnChanges, OnDestroy {
return isInRange(value, this.comparisonStart, this.comparisonEnd, this.isRange);
}

/**
* Gets whether a value is the same as the start and end of the comparison range.
* For context, the functions that we use to determine whether something is the start/end of
* a range don't allow for the start and end to be on the same day, because we'd have to use
* much more specific CSS selectors to style them correctly in all scenarios. This is fine for
* the regular range, because when it happens, the selected styles take over and still show where
* the range would've been, however we don't have these selected styles for a comparison range.
* This function is used to apply a class that serves the same purpose as the one for selected
* dates, but it only applies in the context of a comparison range.
*/
_isComparisonIdentical(value: number) {
// Note that we don't need to null check the start/end
// here, because the `value` will always be defined.
return this.comparisonStart === this.comparisonEnd && value === this.comparisonStart;
}

/** Gets whether a value is the start of the preview range. */
_isPreviewStart(value: number) {
return isStart(value, this.previewStart, this.previewEnd);
Expand Down
3 changes: 2 additions & 1 deletion tools/public_api_guard/material/datepicker.d.ts
Expand Up @@ -121,6 +121,7 @@ export declare class MatCalendarBody implements OnChanges, OnDestroy {
_isComparisonBridgeEnd(value: number, rowIndex: number, colIndex: number): boolean;
_isComparisonBridgeStart(value: number, rowIndex: number, colIndex: number): boolean;
_isComparisonEnd(value: number): boolean;
_isComparisonIdentical(value: number): boolean;
_isComparisonStart(value: number): boolean;
_isInComparisonRange(value: number): boolean;
_isInPreview(value: number): boolean;
Expand All @@ -129,7 +130,7 @@ export declare class MatCalendarBody implements OnChanges, OnDestroy {
_isPreviewStart(value: number): boolean;
_isRangeEnd(value: number): boolean;
_isRangeStart(value: number): boolean;
_isSelected(cell: MatCalendarCell): boolean;
_isSelected(value: number): boolean;
ngOnChanges(changes: SimpleChanges): void;
ngOnDestroy(): void;
static ɵcmp: i0.ɵɵComponentDefWithMeta<MatCalendarBody, "[mat-calendar-body]", ["matCalendarBody"], { "label": "label"; "rows": "rows"; "todayValue": "todayValue"; "startValue": "startValue"; "endValue": "endValue"; "labelMinRequiredCells": "labelMinRequiredCells"; "numCols": "numCols"; "activeCell": "activeCell"; "isRange": "isRange"; "cellAspectRatio": "cellAspectRatio"; "comparisonStart": "comparisonStart"; "comparisonEnd": "comparisonEnd"; "previewStart": "previewStart"; "previewEnd": "previewEnd"; }, { "selectedValueChange": "selectedValueChange"; "previewChange": "previewChange"; }, never, never>;
Expand Down

0 comments on commit 0433cd9

Please sign in to comment.