diff --git a/projects/demo-playwright/tests/kit/input-date-range/input-date-range.spec.ts b/projects/demo-playwright/tests/kit/input-date-range/input-date-range.spec.ts index cf2f05fe6cda..9c11ac0863f7 100644 --- a/projects/demo-playwright/tests/kit/input-date-range/input-date-range.spec.ts +++ b/projects/demo-playwright/tests/kit/input-date-range/input-date-range.spec.ts @@ -13,6 +13,7 @@ import { test.describe('InputDateRange', () => { let example!: Locator; let inputDateRange!: TuiInputDateRangePO; + let documentationPage!: TuiDocumentationPagePO; test.use({ viewport: {width: 650, height: 650}, @@ -21,7 +22,8 @@ test.describe('InputDateRange', () => { test.beforeEach(async ({page}) => { await tuiGoto(page, DemoRoute.InputDateRange); - example = new TuiDocumentationPagePO(page).apiPageExample; + documentationPage = new TuiDocumentationPagePO(page); + example = documentationPage.apiPageExample; inputDateRange = new TuiInputDateRangePO(example.locator('tui-input-date-range')); }); @@ -161,4 +163,26 @@ test.describe('InputDateRange', () => { ); }); }); + + test.describe('Examples', () => { + test('Select second same range => after close/open calendar displays selected period displays correctly', async () => { + const example = documentationPage.getExample('#custom-period'); + + const inputDateRange = new TuiInputDateRangePO( + example.locator('tui-input-date-range'), + ); + + await inputDateRange.textfield.click(); + await inputDateRange.selectItem(2); + await inputDateRange.textfield.click(); + + expect(await inputDateRange.itemHasCheckmark(1)).toBeFalsy(); + expect(await inputDateRange.itemHasCheckmark(2)).toBeTruthy(); + + await expect(inputDateRange.textfield).toHaveValue('Yet another yesterday'); + await expect(inputDateRange.calendarRange).toHaveScreenshot( + '08-calendar-correct-selected-period-after-close-open.png', + ); + }); + }); }); diff --git a/projects/demo-playwright/utils/page-objects/input-date-range.po.ts b/projects/demo-playwright/utils/page-objects/input-date-range.po.ts index 4f61e66b227d..bb724c0aceee 100644 --- a/projects/demo-playwright/utils/page-objects/input-date-range.po.ts +++ b/projects/demo-playwright/utils/page-objects/input-date-range.po.ts @@ -28,4 +28,14 @@ export class TuiInputDateRangePO { await items[index].click(); } + + public async itemHasCheckmark(index: number): Promise { + const items = await this.getItems(); + + const itemCheckmark = await items[index] + .locator('[automation-id="tui-calendar-range__checkmark"]') + .count(); + + return !!itemCheckmark; + } } diff --git a/projects/demo/src/modules/components/input-date-range/examples/5/index.ts b/projects/demo/src/modules/components/input-date-range/examples/5/index.ts index 64b4177dd78f..7f290ca04e7b 100644 --- a/projects/demo/src/modules/components/input-date-range/examples/5/index.ts +++ b/projects/demo/src/modules/components/input-date-range/examples/5/index.ts @@ -30,5 +30,10 @@ export default class Example { 'Yesterday', ({$implicit}) => `Yesterday (${$implicit.from})`, ), + new TuiDayRangePeriod( + new TuiDayRange(yesterday, yesterday), + 'Yet another yesterday', + ({$implicit}) => `Yet another yesterday (${$implicit.from})`, + ), ]; } diff --git a/projects/kit/components/calendar-range/calendar-range.component.ts b/projects/kit/components/calendar-range/calendar-range.component.ts index 9bd2ce183fe0..62f78232be11 100644 --- a/projects/kit/components/calendar-range/calendar-range.component.ts +++ b/projects/kit/components/calendar-range/calendar-range.component.ts @@ -45,6 +45,7 @@ import type {TuiDayRangePeriod} from './day-range-period'; export class TuiCalendarRange implements OnChanges { protected readonly otherDateText$ = inject(TUI_OTHER_DATE_TEXT); protected readonly icons = inject(TUI_COMMON_ICONS); + protected readonly cdr = inject(ChangeDetectorRef); protected previousValue: TuiDayRange | null = null; protected hoveredItem: TuiDay | null = null; protected selectedActivePeriod: TuiDayRangePeriod | null = null; @@ -82,7 +83,7 @@ export class TuiCalendarRange implements OnChanges { constructor() { inject>(TUI_CALENDAR_DATE_STREAM, {optional: true}) - ?.pipe(tuiWatch(inject(ChangeDetectorRef)), takeUntilDestroyed()) + ?.pipe(tuiWatch(this.cdr), takeUntilDestroyed()) .subscribe(value => { this.value = value; }); @@ -143,11 +144,11 @@ export class TuiCalendarRange implements OnChanges { protected onItemSelect(item: TuiDayRangePeriod | string): void { if (!tuiIsString(item)) { - this.updateValue(item.range.dayLimit(this.min, this.max)); this.selectedActivePeriod = item; + this.updateValue(item.range.dayLimit(this.min, this.max)); } else if (this.activePeriod !== null) { - this.updateValue(null); this.selectedActivePeriod = null; + this.updateValue(null); } } diff --git a/projects/legacy/components/input-date-range/input-date-range.component.ts b/projects/legacy/components/input-date-range/input-date-range.component.ts index 7b381bb07d7f..a4ecb0455648 100644 --- a/projects/legacy/components/input-date-range/input-date-range.component.ts +++ b/projects/legacy/components/input-date-range/input-date-range.component.ts @@ -1,6 +1,8 @@ +import type {AfterViewChecked} from '@angular/core'; import { ChangeDetectionStrategy, Component, + forwardRef, HostBinding, HostListener, inject, @@ -35,7 +37,10 @@ import { import type {TuiMarkerHandler} from '@taiga-ui/core/components/calendar'; import {TUI_DATE_FORMAT, TUI_DEFAULT_DATE_FORMAT} from '@taiga-ui/core/tokens'; import type {TuiSizeL, TuiSizeS} from '@taiga-ui/core/types'; -import type {TuiDayRangePeriod} from '@taiga-ui/kit/components/calendar-range'; +import { + TuiCalendarRange, + type TuiDayRangePeriod, +} from '@taiga-ui/kit/components/calendar-range'; import type {TuiInputDateOptions} from '@taiga-ui/kit/tokens'; import { TUI_DATE_RANGE_VALUE_TRANSFORMER, @@ -75,11 +80,14 @@ import {map} from 'rxjs'; }) export class TuiInputDateRangeComponent extends AbstractTuiNullableControl - implements TuiFocusableElementAccessor + implements TuiFocusableElementAccessor, AfterViewChecked { @ViewChild(TuiPrimitiveTextfieldComponent) private readonly textfield?: TuiPrimitiveTextfieldComponent; + @ViewChild(forwardRef(() => TuiCalendarRange)) + private readonly calendarRange?: TuiCalendarRange; + private readonly isMobile = inject(TUI_IS_MOBILE); private readonly mobileCalendar = inject(TUI_MOBILE_CALENDAR, {optional: true}); private readonly options = inject(TUI_INPUT_DATE_OPTIONS); @@ -107,6 +115,8 @@ export class TuiInputDateRangeComponent this.dateFormat = format; }); + protected selectedActivePeriod: TuiDayRangePeriod | null = null; + @Input() public disabledItemHandler: TuiBooleanHandler = TUI_FALSE_HANDLER; @@ -162,7 +172,17 @@ export class TuiInputDateRangeComponent @HostListener('click') public onClick(): void { if (!this.isMobile && this.interactive) { - this.open = !this.open; + this.toggle(); + } + } + + // TODO: remove this after refactor controls to hold whole TuiDayRangePeriod + public ngAfterViewChecked(): void { + if (this.calendarRange) { + // @ts-ignore + this.calendarRange.selectedActivePeriod = this.selectedActivePeriod; + // @ts-ignore + this.calendarRange.cdr.markForCheck(); } } @@ -179,6 +199,10 @@ export class TuiInputDateRangeComponent value.length === DATE_RANGE_FILLER_LENGTH ? TuiDayRange.normalizeParse(value, this.dateFormat.mode) : null; + + if (!this.value) { + this.selectedActivePeriod = null; + } } public onRangeChange(range: TuiDayRange | null): void { @@ -190,6 +214,8 @@ export class TuiInputDateRangeComponent } this.value = range; + // @ts-ignore + this.selectedActivePeriod = this.calendarRange?.selectedActivePeriod ?? null; } public override writeValue(value: TuiDayRange | null): void { @@ -225,7 +251,8 @@ export class TuiInputDateRangeComponent protected get activePeriod(): TuiDayRangePeriod | null { return ( - this.items.find(item => + this.selectedActivePeriod ?? + (this.items.find(item => tuiNullableSame( this.value, item.range, @@ -233,7 +260,8 @@ export class TuiInputDateRangeComponent a.from.daySame(b.from.dayLimit(this.min, this.max)) && a.to.daySame(b.to.dayLimit(this.min, this.max)), ), - ) || null + ) || + null) ); } @@ -273,7 +301,7 @@ export class TuiInputDateRangeComponent protected onIconClick(): void { if (this.isMobile && this.interactive) { - this.open = true; + this.onOpenChange(true); } } diff --git a/projects/legacy/components/input-date-range/test/input-date-range.component.spec.ts b/projects/legacy/components/input-date-range/test/input-date-range.component.spec.ts index b497c0afdd9f..938f05a2dabf 100644 --- a/projects/legacy/components/input-date-range/test/input-date-range.component.spec.ts +++ b/projects/legacy/components/input-date-range/test/input-date-range.component.spec.ts @@ -13,10 +13,10 @@ import { } from '@taiga-ui/cdk'; import {TUI_DATE_FORMAT, TuiRoot} from '@taiga-ui/core'; import {NG_EVENT_PLUGINS} from '@taiga-ui/event-plugins'; -import type {TuiDayRangePeriod} from '@taiga-ui/kit'; import { TUI_DATE_RANGE_VALUE_TRANSFORMER, TUI_DATE_VALUE_TRANSFORMER, + TuiDayRangePeriod, } from '@taiga-ui/kit'; import { TuiInputDateRangeComponent, @@ -93,6 +93,28 @@ describe('InputDateRangeComponent', () => { initializeEnvironment(); }); + it('When switching between ranges with same date, displays appropriate input value', async () => { + const today = TuiDay.currentLocal(); + const previousMonth = today.append({month: -1}); + const first = '1'; + const second = '2'; + + testComponent.items = [ + new TuiDayRangePeriod(new TuiDayRange(previousMonth, today), first), + new TuiDayRangePeriod(new TuiDayRange(previousMonth, today), second), + ]; + fixture.detectChanges(); + + clickOnTextfield(); + + getCalendarItems()[1]?.nativeElement.click(); + fixture.detectChanges(); + + await fixture.whenStable(); + + expect(inputPO.value).toBe(second); + }); + describe('Click on the input field', () => { it('opens the calendar', () => { clickOnTextfield(); @@ -442,6 +464,10 @@ describe('InputDateRangeComponent', () => { return fixture.debugElement.query(By.css('tui-calendar-range')); } + function getCalendarItems(): DebugElement[] { + return pageObject.getAllByAutomationId('tui-calendar-range__menu__item'); + } + function getTextfield(): DebugElement | null { return pageObject.getByAutomationId('tui-input-date-range__textfield'); }