From a8e1f2d6b520daba3f0caeec4f8204fdae1bd8d1 Mon Sep 17 00:00:00 2001 From: teodosiah Date: Fri, 23 Oct 2026 14:43:18 +0300 Subject: [PATCH 1/2] feat(calendar): add day cell template Add the igxCalendarDay directive and the dayTemplate input to customize the content of the calendar day cells. The template receives the date and the day states through IgxCalendarDayTemplateContext, while the cells keep their selection, keyboard navigation, ARIA and styling. The date picker and date range picker pass the template to their calendar. Closes #17822 Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 5 + projects/igniteui-angular/calendar/README.md | 54 ++++ .../src/calendar/calendar.component.html | 1 + .../src/calendar/calendar.component.spec.ts | 290 +++++++++++++++++- .../src/calendar/calendar.component.ts | 35 +++ .../src/calendar/calendar.directives.ts | 77 +++++ .../calendar/days-view/day-item.component.ts | 28 ++ .../days-view/days-view.component.html | 8 +- .../calendar/days-view/days-view.component.ts | 19 +- .../calendar/src/calendar/public_api.ts | 5 +- .../date-picker/date-picker.component.spec.ts | 17 +- .../src/date-picker/date-picker.component.ts | 7 +- .../date-range-picker.component.spec.ts | 13 +- .../date-range-picker.component.ts | 6 + .../references/form-controls.md | 13 + src/app/calendar/calendar.sample.html | 63 ++++ src/app/calendar/calendar.sample.scss | 57 ++++ src/app/calendar/calendar.sample.ts | 20 ++ 18 files changed, 706 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9ee8b113f9..12ac0bae6c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes for each version of this project will be documented in this ## Unreleased +### New Features + +- `IgxCalendarComponent` + - Added the `igxCalendarDay` template directive and the `dayTemplate` input to customize the content of the day cells, e.g. to show event indicators, availability or prices. The template receives the date and the day states (`selected`, `disabled`, `special`, `today`, `weekend`, `currentMonth`, `active` and the range states) through the `IgxCalendarDayTemplateContext`. The day cells keep their selection, keyboard navigation, ARIA attributes and theme styling. `IgxDatePickerComponent` and `IgxDateRangePickerComponent` pass an `igxCalendarDay` template to their calendar. + ### Behavioral Changes - `IgxCarouselComponent` diff --git a/projects/igniteui-angular/calendar/README.md b/projects/igniteui-angular/calendar/README.md index 800a9aa9e80..973e0e8e7db 100644 --- a/projects/igniteui-angular/calendar/README.md +++ b/projects/igniteui-angular/calendar/README.md @@ -167,6 +167,9 @@ Controls the number of month views displayed. Default is 1. - `hideOusideDays: boolean` Controls the visibility of the dates that do not belong to the current month. +- `dayTemplate: TemplateRef | null` +Sets a template for the content of the day cells. When not set, the template marked with the `igxCalendarDay` directive is used, if any. See [Day template](#day-template). + ### Outputs @@ -259,3 +262,54 @@ In the `igxCalendarSubheader` context this is the same as the `viewDate` (offset ``` where `value` is the locale string representation of the date component, `literal` is the locale string separator (if any), and `combined` is as the name suggests the combined output of the two. + +#### Day template + +Decorate a `ng-template` inside the calendar with the `igxCalendarDay` directive to customize the content of the day cells, +for example to show event indicators, availability or prices. The template can also be set through the `dayTemplate` input. + +```html + + + + {{ date.getDate() }} + @if (hasEvents(date)) { + + } + + + +``` + +```scss +.day__dot { + // `currentColor` follows the day cell foreground, so the dot stays visible on the selected background. + background: currentColor; +} +``` + +The template replaces only the default day number. The day cell keeps the selection, the keyboard navigation, +the ARIA attributes and the state styling (selected, range, current, special, disabled, etc.), so selecting a +templated day works as before and the theme still draws its selected state. The template is not rendered for hidden days +(see `hideOutsideDays`) and is used in every month view when `monthsViewNumber` is greater than `1`. +The `igx-date-picker` and `igx-date-range-picker` pass a template marked with `igxCalendarDay` to their calendar. + +**NOTE:** The template content is hidden from assistive technologies, as the day cell is announced through its own +`aria-label`, and it should not contain focusable or interactive elements, since a click anywhere in the cell selects the date. +Use `currentColor` or the `selected` context property to keep the content readable on the selected background. + +| Name | Type | Description | +| :----------------- | :-----: | :---------------------------------------------------------------------------------- | +| $implicit, date | Date | The date of the day cell. | +| formattedDate | string | The default text of the day cell, formatted with `formatOptions` and `formatViews`. | +| selected | boolean | Whether the day is rendered as selected. | +| disabled | boolean | Whether the day is disabled through `disabledDates`. | +| special | boolean | Whether the day is marked as special through `specialDates`. | +| today | boolean | Whether the day is the current date. | +| weekend | boolean | Whether the day is a weekend day. | +| currentMonth | boolean | Whether the day belongs to the displayed month. | +| active | boolean | Whether the day is the active (keyboard focused) day. | +| firstInRange | boolean | Whether the day is the first day of the selected or previewed range. | +| lastInRange | boolean | Whether the day is the last day of the selected or previewed range. | +| withinRange | boolean | Whether the day is within the selected range. | +| withinPreviewRange | boolean | Whether the day is within the previewed range while a range is being selected. | diff --git a/projects/igniteui-angular/calendar/src/calendar/calendar.component.html b/projects/igniteui-angular/calendar/src/calendar/calendar.component.html index cca6233920a..fd3eef60eed 100644 --- a/projects/igniteui-angular/calendar/src/calendar/calendar.component.html +++ b/projects/igniteui-angular/calendar/src/calendar/calendar.component.html @@ -254,6 +254,7 @@

{ IgxCalendarValueComponent, IgxCalendarTemplatesComponent, IgxCalendarRangeValueComponent, + IgxCalendarDayTemplateComponent, + IgxCalendarDayTemplateInputComponent, ], }).compileComponents(); }); @@ -2695,6 +2697,201 @@ describe("IgxCalendar - ", () => { } }); }); + + describe("Day template - ", () => { + let fixture: ComponentFixture; + let dom: DebugElement; + let calendar: IgxCalendarComponent; + + const createFixture = async (setup?: (instance: IgxCalendarDayTemplateComponent) => void) => { + fixture = TestBed.createComponent(IgxCalendarDayTemplateComponent); + setup?.(fixture.componentInstance); + fixture.detectChanges(); + await fixture.whenStable(); + fixture.detectChanges(); + dom = fixture.debugElement; + calendar = fixture.componentInstance.calendar; + }; + + const customDay = (date: Date): HTMLElement => + dom.query(By.css(`.custom-day[data-date="${date.toDateString()}"]`))?.nativeElement; + + const dayItem = (date: Date): HTMLElement => customDay(date).closest(HelperTestFunctions.DAY_CSSCLASS); + + it("Should render the day template inside every day cell with the date and formatted date", async () => { + await createFixture(); + + expect(calendar.dayTemplate).not.toBeNull(); + + const days = dom.queryAll(By.css(HelperTestFunctions.DAY_CSSCLASS)); + const customDays = dom.queryAll(By.css(".custom-day")); + expect(customDays.length).toBe(days.length); + + const day = customDay(new Date(2017, 5, 13)); + expect(day.textContent.trim()).toBe("13!"); + expect(day.parentElement.classList).toContain("igx-day-item__inner"); + expect(dayItem(new Date(2017, 5, 13)).getAttribute("aria-label")).toBe(new Date(2017, 5, 13).toDateString()); + }); + + it("Should not render the day template for hidden days", async () => { + await createFixture((instance) => instance.hideOutsideDays = true); + + const customDays = dom.queryAll(By.css(".custom-day")); + expect(customDays.length).toBe(30); + expect(dom.queryAll(By.css(".custom-day--inactive")).length).toBe(0); + }); + + it("Should pass the day states to the day template", async () => { + await createFixture(); + + expect(customDay(new Date(2017, 5, 13)).classList).toContain("custom-day--selected"); + expect(customDay(new Date(2017, 5, 14)).classList).not.toContain("custom-day--selected"); + expect(customDay(new Date(2017, 5, 21)).classList).toContain("custom-day--disabled"); + expect(customDay(new Date(2017, 5, 2)).classList).toContain("custom-day--special"); + expect(customDay(new Date(2017, 5, 17)).classList).toContain("custom-day--weekend"); + expect(customDay(new Date(2017, 4, 28)).classList).toContain("custom-day--inactive"); + expect(customDay(new Date(2017, 5, 14)).classList).not.toContain("custom-day--inactive"); + }); + + it("Should select a templated day on click and update the template state", async () => { + await createFixture(); + vi.spyOn(calendar.selected, "emit"); + + customDay(new Date(2017, 5, 14)).click(); + fixture.detectChanges(); + + expect(calendar.selected.emit).toHaveBeenCalledWith(new Date(2017, 5, 14)); + expect(calendar.value).toEqual(new Date(2017, 5, 14)); + expect(customDay(new Date(2017, 5, 14)).classList).toContain("custom-day--selected"); + expect(customDay(new Date(2017, 5, 13)).classList).not.toContain("custom-day--selected"); + + const cell = dayItem(new Date(2017, 5, 14)); + expect(cell.getAttribute("aria-selected")).toBe("true"); + expect(cell.classList).toContain(HelperTestFunctions.SELECTED_DATE); + }); + + it("Should keep keyboard navigation and selection working with a day template", async () => { + await createFixture(); + const wrapper = dom.query(By.css(HelperTestFunctions.CALENDAR_WRAPPER_CLASS)).nativeElement; + wrapper.focus(); + calendar.activeDate = new Date(2017, 5, 13); + fixture.detectChanges(); + + UIInteractions.triggerKeyDownEvtUponElem("ArrowRight", wrapper); + fixture.detectChanges(); + + expect(calendar.activeDate).toEqual(new Date(2017, 5, 14)); + expect(customDay(new Date(2017, 5, 14)).classList).toContain("custom-day--active"); + + UIInteractions.triggerKeyDownEvtUponElem("Enter", wrapper); + fixture.detectChanges(); + + expect(calendar.value).toEqual(new Date(2017, 5, 14)); + expect(customDay(new Date(2017, 5, 14)).classList).toContain("custom-day--selected"); + }); + + it("Should update the template state when the value is changed through the API", async () => { + await createFixture(); + + calendar.selectDate(new Date(2017, 5, 26)); + fixture.detectChanges(); + + expect(customDay(new Date(2017, 5, 26)).classList).toContain("custom-day--selected"); + expect(customDay(new Date(2017, 5, 13)).classList).not.toContain("custom-day--selected"); + }); + + it("Should pass the range states to the day template", async () => { + await createFixture((instance) => { + instance.selection = "range"; + instance.model = [new Date(2017, 5, 5), new Date(2017, 5, 8)]; + }); + + const first = customDay(new Date(2017, 5, 5)).classList; + const middle = customDay(new Date(2017, 5, 6)).classList; + const last = customDay(new Date(2017, 5, 8)).classList; + + expect(first).toContain("custom-day--first"); + expect(first).toContain("custom-day--range"); + expect(middle).toContain("custom-day--range"); + expect(middle).not.toContain("custom-day--first"); + expect(middle).not.toContain("custom-day--last"); + expect(last).toContain("custom-day--last"); + expect(customDay(new Date(2017, 5, 9)).classList).not.toContain("custom-day--range"); + }); + + it("Should pass the today state to the day template", async () => { + const today = new Date(); + await createFixture((instance) => { + instance.viewDate = today; + instance.model = today; + }); + + const todayDays = dom.queryAll(By.css(".custom-day--today")); + expect(todayDays.length).toBe(1); + expect(todayDays[0].nativeElement.getAttribute("data-date")).toBe(today.toDateString()); + }); + + it("Should pass the preview range state to the day template while a range is being selected", async () => { + await createFixture((instance) => { + instance.selection = "range"; + instance.model = [new Date(2017, 5, 5)]; + }); + + expect(dom.queryAll(By.css(".custom-day--preview")).length).toBe(0); + + UIInteractions.simulateMouseEvent("mouseenter", customDay(new Date(2017, 5, 8)).parentElement, 0, 0); + fixture.detectChanges(); + + for (const date of [5, 6, 7, 8]) { + expect(customDay(new Date(2017, 5, date)).classList).toContain("custom-day--preview"); + } + expect(customDay(new Date(2017, 5, 9)).classList).not.toContain("custom-day--preview"); + + UIInteractions.simulateMouseEvent("mouseleave", customDay(new Date(2017, 5, 8)).parentElement, 0, 0); + fixture.detectChanges(); + + expect(dom.queryAll(By.css(".custom-day--preview")).length).toBe(0); + }); + + it("Should give the dayTemplate input priority over the igxCalendarDay template", async () => { + await createFixture(); + expect(dom.queryAll(By.css(".override-day")).length).toBe(0); + + fixture.componentInstance.customTemplate = fixture.componentInstance.overrideTemplate; + fixture.detectChanges(); + + expect(calendar.dayTemplate).toBe(fixture.componentInstance.overrideTemplate); + expect(dom.queryAll(By.css(".custom-day")).length).toBe(0); + expect(dom.queryAll(By.css(".override-day")).length) + .toBe(dom.queryAll(By.css(HelperTestFunctions.DAY_CSSCLASS)).length); + + fixture.componentInstance.customTemplate = null; + fixture.detectChanges(); + + expect(dom.queryAll(By.css(".override-day")).length).toBe(0); + expect(dom.queryAll(By.css(".custom-day")).length).toBeGreaterThan(0); + }); + + it("Should render the day template in every month view", async () => { + await createFixture((instance) => instance.monthsViewNumber = 2); + + const views = dom.queryAll(By.css(HelperTestFunctions.DAYS_VIEW)); + expect(views.length).toBe(2); + views.forEach((view) => expect(view.queryAll(By.css(".custom-day")).length).toBeGreaterThan(0)); + expect(customDay(new Date(2017, 6, 20))).toBeTruthy(); + }); + + it("Should render a day template passed through the dayTemplate input", () => { + const inputFixture = TestBed.createComponent(IgxCalendarDayTemplateInputComponent); + inputFixture.detectChanges(); + + const days = inputFixture.debugElement.queryAll(By.css(HelperTestFunctions.DAY_CSSCLASS)); + const customDays = inputFixture.debugElement.queryAll(By.css(".input-day")); + + expect(inputFixture.componentInstance.calendar.dayTemplate).toBe(inputFixture.componentInstance.template); + expect(customDays.length).toBe(days.length); + }); + }); }); }); @@ -2798,6 +2995,95 @@ export class IgxCalendarTemplatesComponent { public viewDate = new Date(2017, 5, 13); } +@Component({ + template: ` + + + {{ formattedDate }}! + + + + {{ date.getDate() }} + + `, + changeDetection: ChangeDetectionStrategy.Eager, + imports: [IgxCalendarComponent, IgxCalendarDayTemplateDirective, FormsModule] +}) +export class IgxCalendarDayTemplateComponent { + @ViewChild(IgxCalendarComponent, { static: true }) + public calendar: IgxCalendarComponent; + @ViewChild("overrideTemplate", { static: true }) + public overrideTemplate: TemplateRef; + public customTemplate: TemplateRef | null = null; + public viewDate = new Date(2017, 5, 13); + public selection: "single" | "multi" | "range" = "single"; + public model: Date | Date[] = new Date(2017, 5, 13); + public monthsViewNumber = 1; + public hideOutsideDays = false; + public disabledDates: DateRangeDescriptor[] = [ + { type: DateRangeType.Between, dateRange: [new Date(2017, 5, 20), new Date(2017, 5, 22)] }, + ]; + public specialDates: DateRangeDescriptor[] = [ + { type: DateRangeType.Specific, dateRange: [new Date(2017, 5, 2)] }, + ]; +} + +@Component({ + template: ` + + + {{ date.getDate() }} + + `, + changeDetection: ChangeDetectionStrategy.Eager, + imports: [IgxCalendarComponent] +}) +export class IgxCalendarDayTemplateInputComponent { + @ViewChild(IgxCalendarComponent, { static: true }) + public calendar: IgxCalendarComponent; + @ViewChild("dayTemplate", { static: true }) + public template: TemplateRef; + public viewDate = new Date(2017, 5, 13); +} + @Component({ template: ``, changeDetection: ChangeDetectionStrategy.Eager, diff --git a/projects/igniteui-angular/calendar/src/calendar/calendar.component.ts b/projects/igniteui-angular/calendar/src/calendar/calendar.component.ts index 0a8b684db4a..d55d33dcac8 100644 --- a/projects/igniteui-angular/calendar/src/calendar/calendar.component.ts +++ b/projects/igniteui-angular/calendar/src/calendar/calendar.component.ts @@ -13,11 +13,14 @@ import { HostListener, ChangeDetectionStrategy, ViewEncapsulation, + TemplateRef, } from '@angular/core'; import { NgTemplateOutlet } from '@angular/common'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { + IgxCalendarDayTemplateContext, + IgxCalendarDayTemplateDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective, @@ -357,6 +360,29 @@ export class IgxCalendarComponent extends IgxCalendarBaseDirective implements Af this.subheaderTemplateDirective = directive; } + /** + * Sets/gets the template used for the content of the day cells. + * + * @remarks + * When not set, the template marked with the `igxCalendarDay` directive is used, if any. + * The template receives an `IgxCalendarDayTemplateContext` and replaces the default day number. + * Selection, keyboard navigation, ARIA attributes and state styling of the day cells are preserved. + * + * @example + * ```html + * + * {{ date.getDate() }} + * ``` + */ + @Input() + public get dayTemplate(): TemplateRef | null { + return this._dayTemplate ?? this.dayTemplateDirective ?? null; + } + + public set dayTemplate(value: TemplateRef | null) { + this._dayTemplate = value; + } + /** * Gets the context for the template marked with the `igxCalendarHeader` directive. * @@ -414,6 +440,15 @@ export class IgxCalendarComponent extends IgxCalendarBaseDirective implements Af @ContentChild(forwardRef(() => IgxCalendarSubheaderTemplateDirective), { read: IgxCalendarSubheaderTemplateDirective, static: true }) private subheaderTemplateDirective!: IgxCalendarSubheaderTemplateDirective; + /** + * @hidden + * @internal + */ + @ContentChild(forwardRef(() => IgxCalendarDayTemplateDirective), { read: TemplateRef }) + private dayTemplateDirective?: TemplateRef; + + private _dayTemplate: TemplateRef | null = null; + /** * @hidden * @internal diff --git a/projects/igniteui-angular/calendar/src/calendar/calendar.directives.ts b/projects/igniteui-angular/calendar/src/calendar/calendar.directives.ts index ff104af80ea..f827f8b49de 100644 --- a/projects/igniteui-angular/calendar/src/calendar/calendar.directives.ts +++ b/projects/igniteui-angular/calendar/src/calendar/calendar.directives.ts @@ -134,6 +134,83 @@ export class IgxCalendarSubheaderTemplateDirective { public template = inject>(TemplateRef); } +/** + * The context passed to a template marked with the `igxCalendarDay` directive. + */ +export interface IgxCalendarDayTemplateContext { + /** The date of the day cell. */ + $implicit: Date; + /** The date of the day cell. */ + date: Date; + /** The default text of the day cell, formatted with the calendar `formatOptions` and `formatViews`. */ + formattedDate: string; + /** Whether the day is rendered as selected. */ + selected: boolean; + /** Whether the day is disabled through `disabledDates`. */ + disabled: boolean; + /** Whether the day is marked as special through `specialDates`. */ + special: boolean; + /** Whether the day is the current date. */ + today: boolean; + /** Whether the day is a weekend day. */ + weekend: boolean; + /** Whether the day belongs to the displayed month. */ + currentMonth: boolean; + /** Whether the day is the active (keyboard focused) day. */ + active: boolean; + /** Whether the day is the first day of the selected or previewed range. */ + firstInRange: boolean; + /** Whether the day is the last day of the selected or previewed range. */ + lastInRange: boolean; + /** Whether the day is within the selected range. */ + withinRange: boolean; + /** Whether the day is within the previewed range while a range is being selected. */ + withinPreviewRange: boolean; +} + +/** + * Marks an `ng-template` as the content template of the calendar day cells. + * + * @remarks + * The template replaces the default content (the day number) of each visible day cell. + * Selection, keyboard navigation, ARIA attributes and state styling stay on the day cell. + * The template content is hidden from assistive technologies and should not contain focusable elements. + * + * @example + * ```html + * + * + * {{ date.getDate() }} + * + * + * ``` + */ +@Directive({ + selector: '[igxCalendarDay]', + standalone: true +}) +export class IgxCalendarDayTemplateDirective { + public template = inject>(TemplateRef); + + /** + * Narrows the type of the template context. + * + * @param _directive The directive instance. + * @param _context The template context. + * @returns Always `true`. + * @example + * ```html + * {{ date.getDate() }} + * ``` + */ + public static ngTemplateContextGuard( + _directive: IgxCalendarDayTemplateDirective, + _context: unknown + ): _context is IgxCalendarDayTemplateContext { + return true; + } +} + /** * @hidden */ diff --git a/projects/igniteui-angular/calendar/src/calendar/days-view/day-item.component.ts b/projects/igniteui-angular/calendar/src/calendar/days-view/day-item.component.ts index e68c6615ea0..c8c196c01b7 100644 --- a/projects/igniteui-angular/calendar/src/calendar/days-view/day-item.component.ts +++ b/projects/igniteui-angular/calendar/src/calendar/days-view/day-item.component.ts @@ -1,5 +1,6 @@ import { Component, Input, Output, EventEmitter, HostBinding, ElementRef, booleanAttribute, ChangeDetectionStrategy, inject } from '@angular/core'; import { CalendarSelection } from '../calendar'; +import { IgxCalendarDayTemplateContext } from '../calendar.directives'; import { areSameMonth, CalendarDay, DateRangeDescriptor, isDateInRanges, isNextMonth, isPreviousMonth } from 'igniteui-angular/core'; /** @@ -190,5 +191,32 @@ export class IgxDayItemComponent { return this.selection !== CalendarSelection.RANGE; } + /** + * Returns the context for the day template. + * The same object is updated and returned on every call to avoid re-creating it on each change detection. + * + * @hidden + * @internal + */ + public getTemplateContext(formattedDate: string): IgxCalendarDayTemplateContext { + return Object.assign(this._templateContext, { + $implicit: this.date.native, + date: this.date.native, + formattedDate, + selected: this.isSelectedCSS, + disabled: this.isDisabled, + special: this.isSpecial, + today: this.isToday, + weekend: this.isWeekend, + currentMonth: this.isCurrentMonth, + active: this.isActive, + firstInRange: this.isFirstInRange, + lastInRange: this.isLastInRange, + withinRange: this.isWithinRange, + withinPreviewRange: this.isWithinPreviewRange + }); + } + private _selected = false; + private _templateContext = {} as IgxCalendarDayTemplateContext; } diff --git a/projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.html b/projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.html index e1814966907..6e48871d8ae 100644 --- a/projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.html +++ b/projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.html @@ -94,7 +94,13 @@ (mouseEnter)="changePreviewRange(day.native)" (mouseLeave)="clearPreviewRange()" > - {{ formattedDate(day.native) | dayDigit:formatViews }} + @if (!dayTemplate) { + {{ formattedDate(day.native) | dayDigit:formatViews }} + } @else if (!item.isHidden) { + + } } diff --git a/projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.ts b/projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.ts index 31463d42cb0..ffa8238f55c 100644 --- a/projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.ts +++ b/projects/igniteui-angular/calendar/src/calendar/days-view/days-view.component.ts @@ -12,11 +12,13 @@ import { ChangeDetectorRef, ChangeDetectionStrategy, ViewEncapsulation, + TemplateRef, inject } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { TitleCasePipe } from '@angular/common'; +import { NgTemplateOutlet, TitleCasePipe } from '@angular/common'; import { CalendarSelection, ScrollDirection } from '../../calendar/calendar'; +import { IgxCalendarDayTemplateContext } from '../calendar.directives'; import { IgxDayItemComponent } from './day-item.component'; import { CalendarDay, @@ -51,7 +53,7 @@ let NEXT_ID = 0; styleUrl: 'days-view.component.css', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [IgxDayItemComponent, TitleCasePipe, DayDigitPipe] + imports: [IgxDayItemComponent, NgTemplateOutlet, TitleCasePipe, DayDigitPipe] }) export class IgxDaysViewComponent extends IgxCalendarBaseDirective { protected el = inject(ElementRef); @@ -160,6 +162,19 @@ export class IgxDaysViewComponent extends IgxCalendarBaseDirective { return this._hideTrailingDays ?? this.hideOutsideDays; } + /** + * Sets/gets the template used for the content of the day cells. + * When not set, the day cells display the formatted day number. + * + * @example + * ```html + * + * {{ date.getDate() }} + * ``` + */ + @Input() + public dayTemplate: TemplateRef | null = null; + @Input({ transform: booleanAttribute }) public set showActiveDay(value: boolean) { this._showActiveDay = value; diff --git a/projects/igniteui-angular/calendar/src/calendar/public_api.ts b/projects/igniteui-angular/calendar/src/calendar/public_api.ts index 1de02a55b40..f7715a4f1eb 100644 --- a/projects/igniteui-angular/calendar/src/calendar/public_api.ts +++ b/projects/igniteui-angular/calendar/src/calendar/public_api.ts @@ -1,5 +1,5 @@ import { IgxCalendarComponent } from './calendar.component'; -import { IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarMonthDirective, IgxCalendarSubheaderTemplateDirective, IgxCalendarYearDirective } from './calendar.directives'; +import { IgxCalendarDayTemplateDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarMonthDirective, IgxCalendarSubheaderTemplateDirective, IgxCalendarYearDirective } from './calendar.directives'; import { IgxDaysViewComponent } from './days-view/days-view.component'; import { IgxMonthPickerComponent } from './month-picker/month-picker.component'; import { IgxMonthsViewComponent } from './months-view/months-view.component'; @@ -24,6 +24,7 @@ export const IGX_CALENDAR_DIRECTIVES = [ IgxCalendarHeaderTitleTemplateDirective, IgxCalendarMonthDirective, IgxCalendarYearDirective, - IgxCalendarSubheaderTemplateDirective + IgxCalendarSubheaderTemplateDirective, + IgxCalendarDayTemplateDirective ] as const; diff --git a/projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.spec.ts b/projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.spec.ts index 2ddeaff3892..77ff8235312 100644 --- a/projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.spec.ts +++ b/projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.spec.ts @@ -4,7 +4,7 @@ import { FormField, disabled, form as signalForm, required } from '@angular/form import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { UIInteractions } from '../../../test-utils/ui-interactions.spec'; import { IgxHintDirective, IgxInputGroupComponent, IgxInputState, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective } from '../../../input-group/src/public_api'; -import { IgxCalendarComponent, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective } from '../../../calendar/src/public_api'; +import { IgxCalendarComponent, IgxCalendarDayTemplateDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective } from '../../../calendar/src/public_api'; import { IgxCalendarContainerComponent } from './calendar-container/calendar-container.component'; import { IgxDatePickerComponent } from './date-picker.component'; import { IgxOverlayService, OverlayCancelableEventArgs, OverlayClosingEventArgs, OverlayEventArgs, OverlaySettings, WEEKDAYS } from 'igniteui-angular/core'; @@ -723,6 +723,18 @@ describe('IgxDatePicker', () => { expect(headerElement.nativeElement.textContent.trim()).toBe('Nov'); } }); + + it('Should use the custom template for day cells', async () => { + fixture.componentInstance.datePicker.value = new Date(2024, 10, 11); + fixture.componentInstance.datePicker.open(); + await fixture.whenStable(); + fixture.detectChanges(); + + const days = fixture.debugElement.queryAll(By.css('.igx-day-item:not(.igx-day-item--hidden)')); + const customDays = fixture.debugElement.queryAll(By.css('.igx-day-item__inner .custom-day')); + expect(customDays.length).toBeGreaterThan(0); + expect(customDays.length).toBe(days.length); + }); }); describe('UI Interaction', () => { @@ -1747,9 +1759,10 @@ export class IgxDatePickerWithProjectionsComponent { {{ formatCalendar.year.value }} {{ formatCalendar.month.value }} + {{ date.getDate() }} `, changeDetection: ChangeDetectionStrategy.Eager, - imports: [IgxDatePickerComponent, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective] + imports: [IgxDatePickerComponent, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarDayTemplateDirective] }) export class IgxDatePickerWithTemplatesComponent { @ViewChild(IgxDatePickerComponent) diff --git a/projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.ts b/projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.ts index 3745e44fa7c..fda040d608f 100644 --- a/projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.ts +++ b/projects/igniteui-angular/date-picker/src/date-picker/date-picker.component.ts @@ -15,6 +15,7 @@ import { Output, PipeTransform, Renderer2, + TemplateRef, ViewChild, ViewContainerRef, booleanAttribute, @@ -33,7 +34,7 @@ import { } from '@angular/forms'; import { IgxCalendarComponent, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective, - IFormattingViews, IFormattingOptions + IFormattingViews, IFormattingOptions, IgxCalendarDayTemplateContext, IgxCalendarDayTemplateDirective } from 'igniteui-angular/calendar'; import { IgxLabelDirective, IgxInputState, toInputState, IgxInputGroupComponent, IgxPrefixDirective, IgxInputDirective, IgxSuffixDirective, @@ -437,6 +438,9 @@ export class IgxDatePickerComponent extends PickerBaseDirective implements Contr @ContentChild(IgxCalendarSubheaderTemplateDirective) private subheaderTemplate!: IgxCalendarSubheaderTemplateDirective; + @ContentChild(IgxCalendarDayTemplateDirective, { read: TemplateRef }) + private dayTemplate?: TemplateRef; + @ContentChild(IgxPickerActionsDirective) private pickerActions!: IgxPickerActionsDirective; @@ -957,6 +961,7 @@ export class IgxDatePickerComponent extends PickerBaseDirective implements Contr this._calendar.headerTitleTemplate = this.headerTitleTemplate; this._calendar.headerTemplate = this.headerTemplate; this._calendar.subheaderTemplate = this.subheaderTemplate; + this._calendar.dayTemplate = this.dayTemplate ?? null; this._calendar.headerOrientation = this.headerOrientation; this._calendar.hideOutsideDays = this.hideOutsideDays; this._calendar.monthsViewNumber = this.displayMonthsCount; diff --git a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts index ae2366edc11..a8a34b33599 100644 --- a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts +++ b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts @@ -24,7 +24,7 @@ import { registerLocaleData } from "@angular/common"; import localeJa from "@angular/common/locales/ja"; import localeBg from "@angular/common/locales/bg"; import { CalendarDay } from 'igniteui-angular/core'; -import { IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective } from 'igniteui-angular/calendar'; +import { IgxCalendarDayTemplateDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective } from 'igniteui-angular/calendar'; import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; // The number of milliseconds in one day @@ -2282,6 +2282,13 @@ describe('IgxDateRangePicker', () => { expect(headerElement.nativeElement.textContent.trim()).toBe('Test subheader'); } }); + + it('Should use the custom template for day cells', async () => { + const days = fixture.debugElement.queryAll(By.css('.igx-day-item:not(.igx-day-item--hidden)')); + const customDays = fixture.debugElement.queryAll(By.css('.igx-day-item__inner .custom-day')); + expect(customDays.length).toBeGreaterThan(0); + expect(customDays.length).toBe(days.length); + }); }); it('should render projected clear icons which clear the range on click', () => { @@ -2649,6 +2656,7 @@ export class DateRangeCustomComponent extends DateRangeTestComponent { Test header Test header title Test subheader + {{ date.getDate() }} @@ -2702,7 +2710,8 @@ export class DateRangeCustomComponent extends DateRangeTestComponent { IgxSuffixDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, - IgxCalendarSubheaderTemplateDirective + IgxCalendarSubheaderTemplateDirective, + IgxCalendarDayTemplateDirective ] }) export class DateRangeTemplatesComponent extends DateRangeTestComponent { diff --git a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.ts b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.ts index 70db3ebb920..ad49c0dfc27 100644 --- a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.ts +++ b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.ts @@ -35,6 +35,8 @@ import { filter, takeUntil } from 'rxjs/operators'; import { CalendarSelection, IgxCalendarComponent, + IgxCalendarDayTemplateContext, + IgxCalendarDayTemplateDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective @@ -485,6 +487,9 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective @ContentChild(IgxCalendarSubheaderTemplateDirective) private subheaderTemplate!: IgxCalendarSubheaderTemplateDirective; + @ContentChild(IgxCalendarDayTemplateDirective, { read: TemplateRef }) + private dayTemplate?: TemplateRef; + /** @hidden @internal */ public get dateSeparator(): string { if (this._dateSeparator === null) { @@ -1352,6 +1357,7 @@ export class IgxDateRangePickerComponent extends PickerBaseDirective this._calendar.headerTitleTemplate = this.headerTitleTemplate; this._calendar.headerTemplate = this.headerTemplate; this._calendar.subheaderTemplate = this.subheaderTemplate; + this._calendar.dayTemplate = this.dayTemplate ?? null; this._calendar.headerOrientation = this.headerOrientation; this._calendar.orientation = this.orientation; this._calendar.specialDates = this.specialDates; diff --git a/skills/igniteui-angular-components/references/form-controls.md b/skills/igniteui-angular-components/references/form-controls.md index d49d8f49828..4591e67addf 100644 --- a/skills/igniteui-angular-components/references/form-controls.md +++ b/skills/igniteui-angular-components/references/form-controls.md @@ -148,6 +148,19 @@ import { IgxCalendarComponent } from 'igniteui-angular/calendar'; Selection modes: `'single'`, `'multi'`, `'range'`. +Custom day cell content (event dots, prices, availability) — import `IgxCalendarDayTemplateDirective` from `igniteui-angular/calendar`. The template replaces only the day number; selection, keyboard navigation and ARIA stay on the cell. Also works inside `igx-date-picker` and `igx-date-range-picker`. + +```html + + + {{ date.getDate() }} + @if (hasEvents(date)) { } + + +``` + +Context (`IgxCalendarDayTemplateContext`): `$implicit`/`date`, `formattedDate`, `selected`, `disabled`, `special`, `today`, `weekend`, `currentMonth`, `active`, `firstInRange`, `lastInRange`, `withinRange`, `withinPreviewRange`. Do not put focusable elements in the template (a click anywhere selects the date; the content is `aria-hidden`); style indicators with `currentColor` so they stay visible on the selected background. + ## Checkbox, Radio, Switch ```typescript diff --git a/src/app/calendar/calendar.sample.html b/src/app/calendar/calendar.sample.html index 78b01a126de..1467f48d56d 100644 --- a/src/app/calendar/calendar.sample.html +++ b/src/app/calendar/calendar.sample.html @@ -23,6 +23,69 @@ +
+ Angular Calendar - Day Template + + +
+ 1 event + 2+ events +
+ + Date Range Picker - igxCalendarDay + + + + {{ formattedDate }} + + {{ isSoldOut(date) ? 'full' : '$' + getPrice(date) }} + + + + +
+ + + + {{ formattedDate }} + + @for (event of getEvents(date); track $index) { + + } + + + +
WC Calendar i); + } + + protected getPrice(date: Date): number { + const weekend = date.getDay() === 0 || date.getDay() === 6; + return 80 + ((date.getDate() * 37) % 50) + (weekend ? 40 : 0); + } + + protected isSoldOut(date: Date): boolean { + return date.getDate() % 9 === 0; + } + protected onSelection(event: Date | Date[]) { console.log(`Selected Date(s): ${event}`); } From 1d1dcc3e78a077ca11813147920e60d013fc13a4 Mon Sep 17 00:00:00 2001 From: teodosiah Date: Sun, 25 Oct 2026 12:29:35 +0200 Subject: [PATCH 2/2] fix(calendar): fix day template context and picker collections Co-Authored-By: Claude Opus 5.5 --- projects/igniteui-angular/calendar/README.md | 3 +- .../src/calendar/calendar.component.spec.ts | 30 +++++++++++++++++++ .../calendar/days-view/day-item.component.ts | 4 +-- .../date-picker/date-picker.component.spec.ts | 5 ++-- .../date-picker/src/date-picker/public_api.ts | 5 ++-- .../date-range-picker.component.spec.ts | 17 ++--------- .../src/date-range-picker/public_api.ts | 5 ++-- .../references/form-controls.md | 2 +- 8 files changed, 47 insertions(+), 24 deletions(-) diff --git a/projects/igniteui-angular/calendar/README.md b/projects/igniteui-angular/calendar/README.md index 973e0e8e7db..6ac1dbb0a30 100644 --- a/projects/igniteui-angular/calendar/README.md +++ b/projects/igniteui-angular/calendar/README.md @@ -293,6 +293,7 @@ the ARIA attributes and the state styling (selected, range, current, special, di templated day works as before and the theme still draws its selected state. The template is not rendered for hidden days (see `hideOutsideDays`) and is used in every month view when `monthsViewNumber` is greater than `1`. The `igx-date-picker` and `igx-date-range-picker` pass a template marked with `igxCalendarDay` to their calendar. +The directive is included in `IGX_DATE_PICKER_DIRECTIVES` and `IGX_DATE_RANGE_PICKER_DIRECTIVES`. **NOTE:** The template content is hidden from assistive technologies, as the day cell is announced through its own `aria-label`, and it should not contain focusable or interactive elements, since a click anywhere in the cell selects the date. @@ -311,5 +312,5 @@ Use `currentColor` or the `selected` context property to keep the content readab | active | boolean | Whether the day is the active (keyboard focused) day. | | firstInRange | boolean | Whether the day is the first day of the selected or previewed range. | | lastInRange | boolean | Whether the day is the last day of the selected or previewed range. | -| withinRange | boolean | Whether the day is within the selected range. | +| withinRange | boolean | Whether the day is within the selected range. Always `false` unless `selection` is `range`. | | withinPreviewRange | boolean | Whether the day is within the previewed range while a range is being selected. | diff --git a/projects/igniteui-angular/calendar/src/calendar/calendar.component.spec.ts b/projects/igniteui-angular/calendar/src/calendar/calendar.component.spec.ts index 444851d5257..6262d096e52 100644 --- a/projects/igniteui-angular/calendar/src/calendar/calendar.component.spec.ts +++ b/projects/igniteui-angular/calendar/src/calendar/calendar.component.spec.ts @@ -2819,6 +2819,35 @@ describe("IgxCalendar - ", () => { expect(customDay(new Date(2017, 5, 9)).classList).not.toContain("custom-day--range"); }); + it("Should not pass the range state to the day template for a multi selection", async () => { + await createFixture((instance) => { + instance.selection = "multi"; + instance.model = [new Date(2017, 5, 5), new Date(2017, 5, 8)]; + }); + + expect(customDay(new Date(2017, 5, 5)).classList).toContain("custom-day--selected"); + expect(customDay(new Date(2017, 5, 8)).classList).toContain("custom-day--selected"); + + for (const date of [6, 7]) { + const day = customDay(new Date(2017, 5, date)); + expect(day.classList).not.toContain("custom-day--range"); + expect(day.classList).not.toContain("custom-day--selected"); + expect(dayItem(new Date(2017, 5, date)).classList).not.toContain("igx-day-item--range"); + } + }); + + it("Should pass a boolean selected state to the day template when the selection is empty", async () => { + for (const selection of ["multi", "range"] as const) { + await createFixture((instance) => { + instance.selection = selection; + instance.model = []; + }); + + expect(customDay(new Date(2017, 5, 14)).getAttribute("data-selected")).toBe("false"); + expect(customDay(new Date(2017, 4, 28)).getAttribute("data-selected")).toBe("false"); + } + }); + it("Should pass the today state to the day template", async () => { const today = new Date(); await createFixture((instance) => { @@ -3026,6 +3055,7 @@ export class IgxCalendarTemplatesComponent { {{ date.getDate() }} `, changeDetection: ChangeDetectionStrategy.Eager, - imports: [IgxDatePickerComponent, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarDayTemplateDirective] + imports: [IGX_DATE_PICKER_DIRECTIVES] }) export class IgxDatePickerWithTemplatesComponent { @ViewChild(IgxDatePickerComponent) diff --git a/projects/igniteui-angular/date-picker/src/date-picker/public_api.ts b/projects/igniteui-angular/date-picker/src/date-picker/public_api.ts index ac5f3883392..85cdaababad 100644 --- a/projects/igniteui-angular/date-picker/src/date-picker/public_api.ts +++ b/projects/igniteui-angular/date-picker/src/date-picker/public_api.ts @@ -1,7 +1,7 @@ import { IgxPickerActionsDirective, IgxPickerClearComponent, IgxPickerToggleComponent } from 'igniteui-angular/core'; import { IgxDatePickerComponent } from './date-picker.component'; import { IgxHintDirective, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective } from 'igniteui-angular/input-group'; -import { IgxCalendarHeaderTemplateDirective, IgxCalendarSubheaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective } from 'igniteui-angular/calendar'; +import { IgxCalendarHeaderTemplateDirective, IgxCalendarSubheaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarDayTemplateDirective } from 'igniteui-angular/calendar'; export * from './date-picker.common'; export * from './date-picker.component'; @@ -20,5 +20,6 @@ export const IGX_DATE_PICKER_DIRECTIVES = [ IgxHintDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarSubheaderTemplateDirective, - IgxCalendarHeaderTitleTemplateDirective + IgxCalendarHeaderTitleTemplateDirective, + IgxCalendarDayTemplateDirective ] as const; diff --git a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts index a8a34b33599..0d2b87bd496 100644 --- a/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts +++ b/projects/igniteui-angular/date-picker/src/date-range-picker/date-range-picker.component.spec.ts @@ -13,7 +13,7 @@ import { CancelableEventArgs, WEEKDAYS, DateRangePickerResourceStringsEN, change import { IgxDateRangeSeparatorDirective, IgxDateRangeStartComponent } from './date-range-picker-inputs.common'; import { IgxDateTimeEditorDirective } from '../../../directives/src/directives/date-time-editor/date-time-editor.directive'; import { DateRangeType } from 'igniteui-angular/core'; -import { IgxDateRangePickerComponent, IgxDateRangeEndComponent } from './public_api'; +import { IgxDateRangePickerComponent, IgxDateRangeEndComponent, IGX_DATE_RANGE_PICKER_DIRECTIVES } from './public_api'; import { AutoPositionStrategy, IgxOverlayService } from 'igniteui-angular/core'; import { map, Subject, timer } from 'rxjs'; import { AsyncPipe } from '@angular/common'; @@ -24,7 +24,6 @@ import { registerLocaleData } from "@angular/common"; import localeJa from "@angular/common/locales/ja"; import localeBg from "@angular/common/locales/bg"; import { CalendarDay } from 'igniteui-angular/core'; -import { IgxCalendarDayTemplateDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective } from 'igniteui-angular/calendar'; import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; // The number of milliseconds in one day @@ -2697,21 +2696,11 @@ export class DateRangeCustomComponent extends DateRangeTestComponent { `, changeDetection: ChangeDetectionStrategy.Eager, imports: [ - IgxDateRangePickerComponent, - IgxDateRangeStartComponent, - IgxDateRangeEndComponent, - IgxPickerToggleComponent, - IgxPickerClearComponent, + IGX_DATE_RANGE_PICKER_DIRECTIVES, IgxIconComponent, FormsModule, IgxInputDirective, - IgxDateTimeEditorDirective, - IgxPrefixDirective, - IgxSuffixDirective, - IgxCalendarHeaderTemplateDirective, - IgxCalendarHeaderTitleTemplateDirective, - IgxCalendarSubheaderTemplateDirective, - IgxCalendarDayTemplateDirective + IgxDateTimeEditorDirective ] }) export class DateRangeTemplatesComponent extends DateRangeTestComponent { diff --git a/projects/igniteui-angular/date-picker/src/date-range-picker/public_api.ts b/projects/igniteui-angular/date-picker/src/date-range-picker/public_api.ts index 7997575389e..2b78f9258f9 100644 --- a/projects/igniteui-angular/date-picker/src/date-range-picker/public_api.ts +++ b/projects/igniteui-angular/date-picker/src/date-range-picker/public_api.ts @@ -2,7 +2,7 @@ import { IgxPickerClearComponent, IgxPickerToggleComponent } from 'igniteui-angu import { IgxDateRangeEndComponent, IgxDateRangeSeparatorDirective, IgxDateRangeStartComponent } from './date-range-picker-inputs.common'; import { IgxDateRangePickerComponent } from './date-range-picker.component'; import { IgxHintDirective, IgxLabelDirective, IgxPrefixDirective, IgxSuffixDirective } from 'igniteui-angular/input-group'; -import { IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective } from 'igniteui-angular/calendar'; +import { IgxCalendarHeaderTemplateDirective, IgxCalendarHeaderTitleTemplateDirective, IgxCalendarSubheaderTemplateDirective, IgxCalendarDayTemplateDirective } from 'igniteui-angular/calendar'; export * from './date-range-picker-inputs.common'; export * from './date-range-picker.component'; @@ -21,5 +21,6 @@ export const IGX_DATE_RANGE_PICKER_DIRECTIVES = [ IgxHintDirective, IgxCalendarHeaderTemplateDirective, IgxCalendarSubheaderTemplateDirective, - IgxCalendarHeaderTitleTemplateDirective + IgxCalendarHeaderTitleTemplateDirective, + IgxCalendarDayTemplateDirective ] as const; diff --git a/skills/igniteui-angular-components/references/form-controls.md b/skills/igniteui-angular-components/references/form-controls.md index 4591e67addf..b1233e73f0d 100644 --- a/skills/igniteui-angular-components/references/form-controls.md +++ b/skills/igniteui-angular-components/references/form-controls.md @@ -148,7 +148,7 @@ import { IgxCalendarComponent } from 'igniteui-angular/calendar'; Selection modes: `'single'`, `'multi'`, `'range'`. -Custom day cell content (event dots, prices, availability) — import `IgxCalendarDayTemplateDirective` from `igniteui-angular/calendar`. The template replaces only the day number; selection, keyboard navigation and ARIA stay on the cell. Also works inside `igx-date-picker` and `igx-date-range-picker`. +Custom day cell content (event dots, prices, availability) — import `IgxCalendarDayTemplateDirective` from `igniteui-angular/calendar`. The template replaces only the day number; selection, keyboard navigation and ARIA stay on the cell. Also works inside `igx-date-picker` and `igx-date-range-picker`; the directive is included in `IGX_DATE_PICKER_DIRECTIVES` and `IGX_DATE_RANGE_PICKER_DIRECTIVES`. ```html