diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ad66822d2c..1b02a8488ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,16 @@ # Ignite UI for Angular Change Log All notable changes for each version of this project will be documented in this file. - ## 19.1.0 ### General - `IgxCarousel` - **Behavioral Changes** - the `maximumIndicatorsCount` input property now defaults to `10`. - **Deprecation** - `CarouselIndicatorsOrientation` enum members `top` and `bottom` have been deprecated and will be removed in a future version. Use `start` and `end` instead. +### New Features +- `IgxBanner` + - Introduced a new `expanded` input property, enabling dynamic control over the banner's state. The banner can now be programmatically set to expanded (visible) or collapsed (hidden) both initially and at runtime. Animations will trigger during runtime updates — the **open animation** plays when `expanded` is set to `true`, and the **close animation** plays when set to `false`. However, no animations will trigger when the property is set initially. + - The banner's event lifecycle (`opening`, `opened`, `closing`, `closed`) only triggers through **user interactions** (e.g., clicking to open/close). Programmatic updates using the `expanded` property will not fire any events. + - If the `expanded` property changes during an ongoing animation, the current animation will **stop** and the opposite animation will begin from the **point where the previous animation left off**. For instance, if the open animation (10 seconds) is interrupted at 6 seconds and `expanded` is set to `false`, the close animation (5 seconds) will start from its 3rd second. ## 19.0.0 ### General @@ -77,6 +81,7 @@ All notable changes for each version of this project will be documented in this - `IgxGridState` - When possible the state directive nows reuses the column that already exists on the grid when restoring the state, instead of creating new column instances every time. This removes the need to set any complex objects manually back on the column on `columnInit`. The only instance where this is still necessary is when the column (or its children in case of column groups) have no `field` property so there's no way to uniquely identify the matching column. - Added support for persisting Multi-Row Layout. + ### Themes - **Breaking Change** `Palettes` - All palette colors have been migrated to the [CSS relative colors syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors). This means that color consumed as CSS variables no longer need to be wrapped in an `hsl` function. diff --git a/projects/igniteui-angular/src/lib/banner/banner.component.spec.ts b/projects/igniteui-angular/src/lib/banner/banner.component.spec.ts index 7ec3c76689d..f1099d42607 100644 --- a/projects/igniteui-angular/src/lib/banner/banner.component.spec.ts +++ b/projects/igniteui-angular/src/lib/banner/banner.component.spec.ts @@ -33,7 +33,8 @@ describe('igxBanner', () => { IgxBannerOneButtonComponent, IgxBannerSampleComponent, IgxBannerCustomTemplateComponent, - SimpleBannerEventsComponent + SimpleBannerEventsComponent, + IgxBannerInitializedOpenComponent ] }).compileComponents(); })); @@ -395,6 +396,36 @@ describe('igxBanner', () => { expect(banner.closing.emit).toHaveBeenCalledTimes(2); expect(banner.closed.emit).toHaveBeenCalledTimes(1); })); + + it('Should toggle banner state when expanded property changes', fakeAsync(() => { + const fixture = TestBed.createComponent(IgxBannerInitializedOpenComponent); + fixture.detectChanges(); + const banner = fixture.componentInstance.banner; + + banner.expanded = false; + tick(); + fixture.detectChanges(); + + expect(banner.expanded).toBeFalse(); + + banner.expanded = true; + tick(); + fixture.detectChanges(); + expect(banner.expanded).toBeTrue(); + expect(banner.elementRef.nativeElement.style.display).toEqual('block'); + + banner.expanded = false; + tick(); + fixture.detectChanges(); + expect(banner.expanded).toBeFalse(); + expect(banner.elementRef.nativeElement.style.display).toEqual(''); + + banner.expanded = true; + tick(); + fixture.detectChanges(); + expect(banner.expanded).toBeTrue(); + expect(banner.elementRef.nativeElement.style.display).toEqual('block'); + })); }); describe('Rendering tests: ', () => { @@ -485,6 +516,16 @@ describe('igxBanner', () => { expect(panel.attributes.getNamedItem('role').nodeValue).toEqual('status'); expect(panel.attributes.getNamedItem('aria-live').nodeValue).toEqual('polite'); })); + + it('Should initialize banner as open when expanded is set to true', fakeAsync(() => { + const fixture = TestBed.createComponent(IgxBannerInitializedOpenComponent); + fixture.detectChanges(); + const banner = fixture.componentInstance.banner; + + expect(banner.expanded).toBeTrue(); + expect(banner.elementRef.nativeElement.style.display).toEqual('block'); + expect(banner.elementRef.nativeElement.querySelector('.' + CSS_CLASS_BANNER)).not.toBeNull(); + })); }); const getBaseClassElements = (fixture: ComponentFixture) => { @@ -601,3 +642,19 @@ export class SimpleBannerEventsComponent { event.cancel = this.cancelFlag; } } + +@Component({ + template: ` +
+ + Banner initialized as open. + +
+ `, + standalone: true, + imports: [IgxBannerComponent] +}) +export class IgxBannerInitializedOpenComponent { + @ViewChild(IgxBannerComponent, { static: true }) + public banner: IgxBannerComponent; +} diff --git a/projects/igniteui-angular/src/lib/banner/banner.component.ts b/projects/igniteui-angular/src/lib/banner/banner.component.ts index fb483b9956d..08fc743b568 100644 --- a/projects/igniteui-angular/src/lib/banner/banner.component.ts +++ b/projects/igniteui-angular/src/lib/banner/banner.component.ts @@ -142,13 +142,12 @@ export class IgxBannerComponent implements IToggleView { return this._animationSettings ? this._animationSettings : this._expansionPanel.animationSettings; } - /** + /** * Gets/Sets the resource strings. * * @remarks * By default it uses EN resources. */ - @Input() public set resourceStrings(value: IBannerResourceStrings) { this._resourceStrings = Object.assign({}, this._resourceStrings, value); @@ -157,14 +156,52 @@ export class IgxBannerComponent implements IToggleView { public get resourceStrings(): IBannerResourceStrings { return this._resourceStrings; } + + /** + * Gets/Sets whether the banner is expanded (visible) or collapsed (hidden). + * Defaults to `false`. + * Setting to `true` opens the banner, while `false` closes it. + * + * @example + * // Expand the banner + * banner.expanded = true; + * + * @example + * // Collapse the banner + * banner.expanded = false; + * + * @example + * // Check if the banner is expanded + * const isExpanded = banner.expanded; + */ + @Input() + public get expanded(): boolean { + return this._expanded; + } + + public set expanded(value: boolean) { + if (value === this._expanded) { + return; + } + + this._expanded = value; + this._shouldFireEvent = true; + + if (value) { + this._expansionPanel.open(); + } else { + this._expansionPanel.close(); + } + } + /** - * Gets whether banner is collapsed + * Gets whether the banner is collapsed. * * ```typescript * const isCollapsed: boolean = banner.collapsed; * ``` */ - public get collapsed() { + public get collapsed(): boolean { return this._expansionPanel.collapsed; } @@ -195,6 +232,8 @@ export class IgxBannerComponent implements IToggleView { @ContentChild(IgxBannerActionsDirective) private _bannerActionTemplate: IgxBannerActionsDirective; + private _expanded: boolean = false; + private _shouldFireEvent: boolean = false; private _bannerEvent: BannerEventArgs; private _animationSettings: ToggleAnimationSettings; private _resourceStrings = getCurrentResourceStrings(BannerResourceStringsEN); @@ -216,7 +255,7 @@ export class IgxBannerComponent implements IToggleView { * ``` */ public open(event?: Event) { - this._bannerEvent = { owner: this, event}; + this._bannerEvent = { owner: this, event }; const openingArgs: BannerCancelEventArgs = { owner: this, event, @@ -227,6 +266,8 @@ export class IgxBannerComponent implements IToggleView { return; } this._expansionPanel.open(event); + this._expanded = true; + this._shouldFireEvent = false; } /** @@ -255,6 +296,8 @@ export class IgxBannerComponent implements IToggleView { return; } this._expansionPanel.close(event); + this._expanded = false; + this._shouldFireEvent = false; } /** @@ -281,11 +324,17 @@ export class IgxBannerComponent implements IToggleView { /** @hidden */ public onExpansionPanelOpen() { + if (this._shouldFireEvent) { + return; + } this.opened.emit(this._bannerEvent); } /** @hidden */ public onExpansionPanelClose() { + if (this._shouldFireEvent) { + return; + } this.closed.emit(this._bannerEvent); } } diff --git a/projects/igniteui-angular/src/lib/expansion-panel/expansion-panel.component.ts b/projects/igniteui-angular/src/lib/expansion-panel/expansion-panel.component.ts index ffda084ea10..f2f643adb21 100644 --- a/projects/igniteui-angular/src/lib/expansion-panel/expansion-panel.component.ts +++ b/projects/igniteui-angular/src/lib/expansion-panel/expansion-panel.component.ts @@ -276,7 +276,7 @@ export class IgxExpansionPanelComponent extends ToggleAnimationPlayer implements * ``` */ public expand(evt?: Event) { - if (!this.collapsed) { // If the panel is already opened, do nothing + if (!this.collapsed && !this.closeAnimationPlayer) { // Check if the panel is currently collapsing or already expanded return; } const args = { event: evt, panel: this, owner: this, cancel: false };