• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.45
/projects/igniteui-angular/src/lib/calendar/days-view/days-view.component.ts
1
import {
2
    Component,
3
    Output,
4
    EventEmitter,
5
    Input,
6
    HostListener,
7
    ViewChildren,
8
    QueryList,
9
    HostBinding,
10
    Inject,
11
    LOCALE_ID,
12
    booleanAttribute,
13
    ElementRef,
14
    ChangeDetectorRef,
15
    ChangeDetectionStrategy,
16
} from '@angular/core';
17
import { NG_VALUE_ACCESSOR } from '@angular/forms';
18
import { NgIf, NgFor, TitleCasePipe } from '@angular/common';
19
import { CalendarSelection, ScrollDirection } from '../../calendar/calendar';
20
import { IgxDayItemComponent } from './day-item.component';
21
import { DateRangeType } from '../../core/dates';
22
import { IgxCalendarBaseDirective } from '../calendar-base';
23
import { PlatformUtil, intoChunks } from '../../core/utils';
24
import { IViewChangingEventArgs } from './days-view.interface';
25
import {
26
    areSameMonth,
27
    generateMonth,
28
    getClosestActiveDate,
29
    getNextActiveDate,
30
    getPreviousActiveDate,
31
    isDateInRanges,
32
} from "../common/helpers";
33
import { CalendarDay } from '../common/model';
34

35
let NEXT_ID = 0;
2✔
36

37
@Component({
38
    providers: [
39
        {
40
            multi: true,
41
            provide: NG_VALUE_ACCESSOR,
42
            useExisting: IgxDaysViewComponent
43
        },
44
    ],
45
    selector: 'igx-days-view',
46
    templateUrl: 'days-view.component.html',
47
    changeDetection: ChangeDetectionStrategy.OnPush,
48
    imports: [NgIf, NgFor, IgxDayItemComponent, TitleCasePipe]
49
})
50
export class IgxDaysViewComponent extends IgxCalendarBaseDirective {
2✔
UNCOV
51
    #standalone = true;
×
52

53
    /**
54
     * Sets/gets the `id` of the days view.
55
     * If not set, the `id` will have value `"igx-days-view-0"`.
56
     * ```html
57
     * <igx-days-view id="my-days-view"></igx-days-view>
58
     * ```
59
     * ```typescript
60
     * let daysViewId = this.daysView.id;
61
     * ```
62
     */
63
    @HostBinding('attr.id')
64
    @Input()
UNCOV
65
    public id = `igx-days-view-${NEXT_ID++}`;
×
66

67
    @HostBinding('attr.tabIndex')
68
    @Input()
UNCOV
69
    public tabIndex = 0;
×
70

71
    @HostBinding('attr.role')
72
    @Input()
UNCOV
73
    public role = 'grid';
×
74

75
        @HostBinding('class.igx-days-view')
UNCOV
76
        public readonly viewClass = true;
×
77

78
    @Input()
79
        @HostBinding('class.igx-days-view--standalone')
80
        public get standalone() {
UNCOV
81
        return this.#standalone;
×
82
    }
83

84
        public set standalone(value: boolean) {
UNCOV
85
        this.#standalone = value;
×
86
    }
87

88
    @HostBinding('attr.aria-activeDescendant')
89
    protected get activeDescendant() {
UNCOV
90
        if (this.tabIndex === -1) return;
×
91

UNCOV
92
        return this.activeDate.getTime();
×
93
    }
94

95
    /**
96
     * Show/hide week numbers
97
     *
98
     * @example
99
     * ```html
100
     * <igx-days-view [showWeekNumbers]="true"></igx-days-view>
101
     * ``
102
     */
103
    @Input({ transform: booleanAttribute })
104
    public showWeekNumbers: boolean;
105

106
    /**
107
     * @hidden
108
     * @internal
109
     */
110
    @Input()
111
    public set activeDate(value: Date) {
UNCOV
112
        this._activeDate = value;
×
UNCOV
113
        this.changePreviewRange(value);
×
UNCOV
114
        this.activeDateChange.emit(this._activeDate);
×
115
    }
116

117
    public get activeDate(): Date {
UNCOV
118
        return this._activeDate ?? this.viewDate;
×
119
    }
120

121
    /**
122
     * @hidden
123
     * @internal
124
     */
125
    @Input()
126
    public set previewRangeDate(value: Date) {
UNCOV
127
        this._previewRangeDate = value;
×
UNCOV
128
        this.previewRangeDateChange.emit(value);
×
129
    }
130

131
    public get previewRangeDate() {
UNCOV
132
        return this._previewRangeDate;
×
133
    }
134

135
    @Input({ transform: booleanAttribute })
136
    public set hideLeadingDays(value: boolean) {
UNCOV
137
        this._hideLeadingDays = value;
×
UNCOV
138
        this.cdr.detectChanges();
×
139
    }
140

141
    public get hideLeadingDays() {
UNCOV
142
        return this._hideLeadingDays ?? this.hideOutsideDays;
×
143
    }
144

145
    @Input({ transform: booleanAttribute })
146
    public set hideTrailingDays(value: boolean) {
UNCOV
147
        this._hideTrailingDays = value;
×
UNCOV
148
        this.cdr.detectChanges();
×
149
    }
150

151
    public get hideTrailingDays() {
UNCOV
152
        return this._hideTrailingDays ?? this.hideOutsideDays;
×
153
    }
154

155
    @Input({ transform: booleanAttribute })
156
    public set showActiveDay(value: boolean) {
UNCOV
157
        this._showActiveDay = value;
×
158
    }
159

160
    public get showActiveDay() {
UNCOV
161
        return this._showActiveDay;
×
162
    }
163

164
    /**
165
     * @hidden
166
     */
167
    @Output()
UNCOV
168
    public dateSelected = new EventEmitter<Date>();
×
169

170
    /**
171
     * @hidden
172
     */
173
    @Output()
UNCOV
174
    public pageChanged = new EventEmitter<IViewChangingEventArgs>();
×
175

176
    /**
177
     * @hidden
178
     */
179
    @Output()
UNCOV
180
    public activeDateChange = new EventEmitter<Date>();
×
181

182
    /**
183
     * @hidden
184
     */
185
    @Output()
UNCOV
186
    public previewRangeDateChange = new EventEmitter<any>();
×
187

188
    /**
189
     * @hidden
190
     */
191
    @ViewChildren(IgxDayItemComponent, { read: IgxDayItemComponent })
192
    public dates: QueryList<IgxDayItemComponent>;
193

194
    private _activeDate: Date;
195
    private _previewRangeDate: Date;
196
    private _hideLeadingDays: boolean;
197
    private _hideTrailingDays: boolean;
198
    private _showActiveDay: boolean;
199

200
    /**
201
     * @hidden
202
     */
203
    constructor(
204
        platform: PlatformUtil,
205
        @Inject(LOCALE_ID) _localeId: string,
UNCOV
206
        protected el: ElementRef,
×
UNCOV
207
        public override cdr: ChangeDetectorRef,
×
208
    ) {
UNCOV
209
        super(platform, _localeId, null, cdr);
×
210
    }
211

212
    /**
213
     * @hidden
214
     */
215
    private handleArrowKeydown(event: KeyboardEvent, delta: number) {
UNCOV
216
        event.preventDefault();
×
UNCOV
217
        event.stopPropagation();
×
218

UNCOV
219
        const date = getClosestActiveDate(
×
220
            CalendarDay.from(this.activeDate),
221
            delta,
222
            this.disabledDates,
223
        );
224

UNCOV
225
        if (!areSameMonth(this.activeDate, date.native)) {
×
UNCOV
226
            this.pageChanged.emit({
×
227
                monthAction: delta > 0 ? ScrollDirection.NEXT : ScrollDirection.PREV,
×
228
                key: event.key,
229
                nextDate: date.native
230
            });
231
        }
232

UNCOV
233
        this.activeDate = date.native;
×
UNCOV
234
        this.viewDate = date.native;
×
UNCOV
235
        this.clearPreviewRange();
×
UNCOV
236
        this.changePreviewRange(date.native);
×
UNCOV
237
        this.cdr.detectChanges();
×
238
    }
239

240
    /**
241
     * @hidden
242
     */
243
    @HostListener('keydown.arrowright', ['$event'])
244
    protected onArrowRight(event: KeyboardEvent) {
UNCOV
245
        this.handleArrowKeydown(event, 1);
×
246
    }
247

248
    /**
249
     * @hidden
250
     */
251
    @HostListener('keydown.arrowleft', ['$event'])
252
    protected onArrowLeft(event: KeyboardEvent) {
UNCOV
253
        this.handleArrowKeydown(event, -1);
×
254
    }
255

256
    /**
257
     * @hidden
258
     */
259
    @HostListener('keydown.arrowup', ['$event'])
260
    protected onArrowUp(event: KeyboardEvent) {
UNCOV
261
        this.handleArrowKeydown(event, -7);
×
262
    }
263

264
    /**
265
     * @hidden
266
     */
267
    @HostListener('keydown.arrowdown', ['$event'])
268
    protected onArrowDown(event: KeyboardEvent) {
UNCOV
269
        this.handleArrowKeydown(event, 7);
×
270
    }
271

272
    /**
273
     * @hidden
274
     */
275
    @HostListener('keydown.Space', ['$event'])
276
    @HostListener('keydown.enter', ['$event'])
277
    protected onKeydownEnter(event: KeyboardEvent) {
UNCOV
278
        event.stopPropagation();
×
UNCOV
279
        this.selectActiveDate();
×
280
    }
281

282
    /**
283
     * @hidden
284
     */
285
    @HostListener('keydown.home', ['$event'])
286
    protected onKeydownHome(event: KeyboardEvent) {
UNCOV
287
        event.preventDefault();
×
UNCOV
288
        event.stopPropagation();
×
289

UNCOV
290
        const first = CalendarDay.from(this.activeDate);
×
UNCOV
291
        this.activeDate = getNextActiveDate(
×
292
            first.set({ date: 1 }),
293
            this.disabledDates,
294
        ).native;
295
    }
296

297
    /**
298
     * @hidden
299
     */
300
    @HostListener('keydown.end', ['$event'])
301
    protected onKeydownEnd(event: KeyboardEvent) {
UNCOV
302
        event.preventDefault();
×
UNCOV
303
        event.stopPropagation();
×
304

UNCOV
305
        const last = CalendarDay.from(this.activeDate);
×
UNCOV
306
        this.activeDate = getPreviousActiveDate(
×
307
            last.set({ month: last.month + 1, date: 0 }),
308
            this.disabledDates,
309
        ).native;
310
    }
311

312
    /**
313
     * @hidden
314
     */
315
    @HostListener('focus')
316
    protected handleFocus() {
UNCOV
317
        this._showActiveDay = true;
×
UNCOV
318
        this.changePreviewRange(this.activeDate);
×
319
    }
320

321
    /**
322
     * @hidden
323
     */
324
    @HostListener('blur')
325
    protected handleBlur() {
UNCOV
326
        this._showActiveDay = false;
×
UNCOV
327
        this.clearPreviewRange();
×
328
    }
329

330
    /**
331
     * @hidden
332
     */
333
    protected handleDateClick(item: IgxDayItemComponent) {
UNCOV
334
        const date = item.date.native;
×
335

UNCOV
336
        if (item.isPreviousMonth) {
×
UNCOV
337
            this.pageChanged.emit({
×
338
                monthAction: ScrollDirection.PREV,
339
                key: '',
340
                nextDate: date
341
            });
342
        }
343

UNCOV
344
        if (item.isNextMonth) {
×
UNCOV
345
            this.pageChanged.emit({
×
346
                monthAction: ScrollDirection.NEXT,
347
                key: '',
348
                nextDate: date
349
            });
350
        }
351

UNCOV
352
        if (this.tabIndex !== -1) {
×
UNCOV
353
            this.el.nativeElement.focus();
×
354
        }
355

UNCOV
356
        this.activeDate = item.date.native;
×
UNCOV
357
        this.selectActiveDate();
×
358
    }
359

360
    private selectActiveDate() {
UNCOV
361
        this.selectDate(this.activeDate);
×
UNCOV
362
        this.dateSelected.emit(this.activeDate);
×
UNCOV
363
        this.selected.emit(this.selectedDates);
×
UNCOV
364
        this.clearPreviewRange();
×
365
    }
366

367
    protected get calendarMonth(): CalendarDay[] {
UNCOV
368
        return Array.from(generateMonth(this.viewDate, this.weekStart));
×
369
    }
370

371
    protected get monthWeeks(): CalendarDay[][] {
UNCOV
372
        return Array.from(intoChunks(this.calendarMonth, 7));
×
373
    }
374

375
    /**
376
     * Returns the week number by date
377
     *
378
     * @hidden
379
     */
380
    public getWeekNumber(date: CalendarDay): number {
UNCOV
381
        return date.week;
×
382
    }
383

384
    /**
385
     * Returns the locale representation of the date in the days view.
386
     *
387
     * @hidden
388
     */
389
    public formattedDate(value: Date): string {
UNCOV
390
        if (this.formatViews.day) {
×
391
            return this.formatterDay.format(value);
×
392
        }
393

UNCOV
394
        return `${value.getDate()}`;
×
395
    }
396

397
    /**
398
     * @hidden
399
     */
400
    public get weekHeaderLabels(): {long: string, formatted: string}[] {
UNCOV
401
        const weekdays = [];
×
UNCOV
402
        const rawFormatter = new Intl.DateTimeFormat(this.locale, { weekday: 'long' });
×
403

UNCOV
404
        for (const day of this.monthWeeks.at(0)) {
×
UNCOV
405
            weekdays.push({
×
406
                long: rawFormatter.format(day.native),
407
                formatted: this.formatterWeekday.format(day.native)
408
            });
409
        }
410

UNCOV
411
        return weekdays;
×
412
    }
413

414
    protected get weekNumberHeader(): { short: string, long: string } {
UNCOV
415
        const weekOfYear = (style: 'narrow' | 'long') => {
×
UNCOV
416
            const dn = new Intl.DisplayNames(this.locale, {
×
417
                type: 'dateTimeField',
418
                style,
419
            });
420

UNCOV
421
            return dn.of('weekOfYear');
×
422
        }
423

UNCOV
424
        return {
×
425
            short: weekOfYear('narrow').substring(0, 1),
426
            long: weekOfYear('long'),
427
        }
428
    }
429

430
    /**
431
     * @hidden
432
     */
433
    public rowTracker(index: number, item: CalendarDay[]): string {
UNCOV
434
        return `${item[index].month}${item[index].date}`;
×
435
    }
436

437
    /**
438
     * @hidden
439
     */
440
    public dateTracker(_: number, item: CalendarDay): string {
UNCOV
441
        return `${item.month}--${item.date}`;
×
442
    }
443

444
    /**
445
     * @hidden
446
     */
447
    public isSelected(date: CalendarDay): boolean {
UNCOV
448
        const dates = this.value as Date[];
×
UNCOV
449
        const hasValue = this.value || (Array.isArray(this.value) && this.value.length === 1);
×
450

UNCOV
451
        if (isDateInRanges(date, this.disabledDates)) {
×
UNCOV
452
            return false;
×
453
        }
454

UNCOV
455
        if (this.selection === CalendarSelection.SINGLE) {
×
UNCOV
456
            return !!this.value && date.equalTo(this.value as Date);
×
457
        }
458

UNCOV
459
        if (!hasValue) {
×
460
            return false;
×
461
        }
462

UNCOV
463
        if (this.selection === CalendarSelection.MULTI && dates.length > 0) {
×
UNCOV
464
            return isDateInRanges(date, [
×
465
                {
466
                    type: DateRangeType.Specific,
467
                    dateRange: dates,
468
                },
469
            ]);
470
        }
471

UNCOV
472
        if (this.selection === CalendarSelection.RANGE && dates.length > 0) {
×
UNCOV
473
            return isDateInRanges(date, [
×
474
                {
475
                    type: DateRangeType.Between,
476
                    dateRange: [dates.at(0), dates.at(-1)],
477
                },
478
            ]);
479
        }
480
    }
481

482
    /**
483
     * @hidden
484
     */
485
    protected isFirstInRange(date: CalendarDay): boolean {
UNCOV
486
        const dates = this.selectedDates;
×
487

UNCOV
488
        if (this.isSingleSelection || dates.length === 0) {
×
UNCOV
489
            return false;
×
490
        }
491

UNCOV
492
        let target = dates.at(0);
×
493

UNCOV
494
        if (this.previewRangeDate && this.previewRangeDate < target) {
×
495
            target = this.previewRangeDate;
×
496
        }
497

UNCOV
498
        return date.equalTo(target);
×
499
    }
500

501
    /**
502
     * @hidden
503
     */
504
    protected isLastInRange(date: CalendarDay): boolean {
UNCOV
505
        const dates = this.selectedDates;
×
506

UNCOV
507
        if (this.isSingleSelection || dates.length === 0) {
×
UNCOV
508
            return false;
×
509
        }
510

UNCOV
511
        let target = dates.at(-1);
×
512

UNCOV
513
        if (this.previewRangeDate && this.previewRangeDate > target) {
×
514
            target = this.previewRangeDate;
×
515
        }
516

UNCOV
517
        return date.equalTo(target);
×
518
    }
519

520
    /**
521
     * @hidden
522
     */
523
    protected isActiveDate(day: CalendarDay): boolean {
UNCOV
524
        return this._showActiveDay && day.equalTo(this.activeDate);
×
525
    }
526

527
    /**
528
     * @hidden
529
     */
530
    protected isWithinRange(date: Date, checkForRange: boolean, min?: Date, max?: Date): boolean {
UNCOV
531
        const dates = this.selectedDates;
×
532

UNCOV
533
        if (checkForRange && !(Array.isArray(dates) && dates.length > 1)) {
×
UNCOV
534
            return false;
×
535
        }
536

UNCOV
537
        min = min ? min : dates.at(0);
×
UNCOV
538
        max = max ? max : dates.at(-1);
×
539

UNCOV
540
        return isDateInRanges(date,
×
541
            [
542
                {
543
                    type: DateRangeType.Between,
544
                    dateRange: [min, max]
545
                }
546
            ]
547
        );
548
    }
549

550
    protected isWithinPreviewRange(date: Date): boolean {
UNCOV
551
        if (this.selection !== 'range') return false;
×
552

UNCOV
553
        const dates = this.selectedDates;
×
554

UNCOV
555
        if (!(dates.length > 0 && this.previewRangeDate)) {
×
UNCOV
556
            return false;
×
557
        }
558

559
        return isDateInRanges(date, [
×
560
          {
561
            type: DateRangeType.Between,
562
            dateRange: [dates.at(0), this.previewRangeDate],
563
          },
564
        ]);
565
    }
566

567
    /**
568
     * @hidden
569
     */
570
    private get isSingleSelection(): boolean {
UNCOV
571
        return this.selection !== CalendarSelection.RANGE;
×
572
    }
573

574
    /**
575
     * @hidden @internal
576
     */
577
    public changePreviewRange(date: Date) {
UNCOV
578
        const dates = this.value as Date[];
×
579

UNCOV
580
        if (this.selection === 'range' && dates.length === 1) {
×
UNCOV
581
            const first = CalendarDay.from(dates.at(0));
×
582

UNCOV
583
            if (!first.equalTo(date)) {
×
UNCOV
584
              this.setPreviewRangeDate(date);
×
585
            }
586
        }
587
    }
588

589
    /**
590
     * @hidden @internal
591
     */
592
    public clearPreviewRange() {
UNCOV
593
        if (this.previewRangeDate) {
×
UNCOV
594
            this.setPreviewRangeDate(undefined);
×
595
        }
596
    }
597

598
    private setPreviewRangeDate(value?: Date) {
UNCOV
599
        this.previewRangeDate = value;
×
600
    }
601
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc