Skip to content

Commit

Permalink
Merge pull request #8268 from IgniteUI/vslavov/expansion-panel-cancel…
Browse files Browse the repository at this point in the history
…-interaction

fix(expansion-panel): make header interaction cancelable - master
  • Loading branch information
Lipata authored Oct 7, 2020
2 parents 9b96d6c + 5015ca0 commit 4075879
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 31 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ All notable changes for each version of this project will be documented in this
- **Breaking Change** - Deprecated the `label` property.
- `igxGridActions`
- Added `asMenuItems` Input for grid actions - `igx-grid-editing-actions`, `igx-grid-pinning-actions`. When set to true will render the related action buttons as separate menu items with button and label.
- `IgxExpansionPanel`
- `IExpansionPanelEventArgs.panel` - Deprecated. Usе `owner` property to get a reference to the panel.


### New Features
Expand Down Expand Up @@ -54,6 +56,9 @@ All notable changes for each version of this project will be documented in this
- The component now utilizes the `IgxOverlayService` to position itself in the DOM.
- An additional input property `outlet` has been added to allow users to specify custom Overlay Outlets using the `IgxOverlayOutletDirective`;
- The `position` property now accepts values of type `IgxToastPosition` that work with strict templates.
- `IgxExpansionPanelHeader`
- `onInteraction` is now cancelable
- Added `iconRef` property. This can be used to get a reference to the displayed expand/collapsed indicator. Returns `null` if `iconPosition` is set to `NONE`.

## 10.1.0

Expand Down
7 changes: 4 additions & 3 deletions projects/igniteui-angular/src/lib/expansion-panel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ The following outputs are available in the **igx-expansion-panel** component:

| Name | Cancelable | Description | Parameters
| :--- | :--- | :--- | :--- |
| `onCollapsed` | `false` | Emitted when the panel is collapsed | `{ event: Event, panel: IgxExpansionPanelComponent }` |
| `onExpanded` | `false` | Emitted when the panel is expanded | `{ event: Event, panel: IgxExpansionPanelComponent }` |
| `onCollapsed` | `false` | Emitted when the panel is collapsed | `IExpansionPanelEventArgs` |
| `onExpanded` | `false` | Emitted when the panel is expanded | `IExpansionPanelEventArgs` |


### Methods
Expand All @@ -81,14 +81,15 @@ The following inputs are available in the **igx-expansion-panel-header** compone
| `role` | `string` | The `role` attribute of the header |
| `iconPosition` | `string` | The position of the expand/collapse icon of the header |
| `disabled` | `boolean` | Gets/sets whether the panel header is disabled (blocking user interaction) or not |
| `iconRef` | `ElementRef` | Gets the reference to the element being used as expand/collapse indicator. If `iconPosition` is `NONE` - return `null` |


### Outputs
The following outputs are available in the **igx-expansion-panel-header** component:

| Name | Cancelable | Description | Parameters
| :--- | :--- | :--- | :--- |
| `onInteraction` | `false` | Emitted when a user interacts with the header host | `{ event: Event, panel: IgxExpansionPanelComponent }` |
| `onInteraction` | `true` | Emitted when a user interacts with the header host | `IExpansionPanelCancelableEventArgs` |

## IgxExpansionPanelBodyComponent
### Inputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import {
EventEmitter,
Output,
ContentChild,
Inject
Inject,
ViewChild
} from '@angular/core';
import { IgxExpansionPanelIconDirective } from './expansion-panel.directives';
import { IExpansionPanelEventArgs, IGX_EXPANSION_PANEL_COMPONENT, IgxExpansionPanelBase } from './expansion-panel.common';
import { IGX_EXPANSION_PANEL_COMPONENT, IgxExpansionPanelBase, IExpansionPanelCancelableEventArgs } from './expansion-panel.common';
import { mkenum } from '../core/utils';
import { IgxIconComponent } from '../icon/public_api';

/**
* @hidden
Expand Down Expand Up @@ -42,6 +44,23 @@ export class IgxExpansionPanelHeaderComponent {
*/
public id = '';

/** @hidden @internal */
@ContentChild(IgxExpansionPanelIconDirective, { read: ElementRef })
private customIconRef: ElementRef;

/** @hidden @internal */
@ViewChild(IgxIconComponent, { read: ElementRef })
public defaultIconRef: ElementRef;

/**
* Returns a reference to the `igx-expansion-panel-icon` element;
* If `iconPosition` is `NONE` - return null;
*/
public get iconRef(): ElementRef {
const renderedTemplate = this.customIconRef ?? this.defaultIconRef;
return this.iconPosition !== ICON_POSITION.NONE ? renderedTemplate : null;
}

/**
* @hidden
*/
Expand Down Expand Up @@ -120,7 +139,7 @@ export class IgxExpansionPanelHeaderComponent {
/**
* Emitted whenever a user interacts with the header host
* ```typescript
* handleInteraction(event: IExpansionPanelEventArgs) {
* handleInteraction(event: IExpansionPanelCancelableEventArgs) {
* ...
* }
* ```
Expand All @@ -131,7 +150,7 @@ export class IgxExpansionPanelHeaderComponent {
* ```
*/
@Output()
public onInteraction = new EventEmitter<IExpansionPanelEventArgs>();
public onInteraction = new EventEmitter<IExpansionPanelCancelableEventArgs >();

/**
* @hidden
Expand Down Expand Up @@ -185,7 +204,11 @@ export class IgxExpansionPanelHeaderComponent {
evt.stopPropagation();
return;
}
this.onInteraction.emit({ event: evt, panel: this.panel });
const eventArgs: IExpansionPanelCancelableEventArgs = { event: evt, panel: this.panel, owner: this.panel, cancel: false };
this.onInteraction.emit(eventArgs);
if (eventArgs.cancel === true) {
return;
}
this.panel.toggle(evt);
evt.preventDefault();
}
Expand All @@ -194,17 +217,25 @@ export class IgxExpansionPanelHeaderComponent {
@HostListener('keydown.Alt.ArrowDown', ['$event'])
public openPanel(event: KeyboardEvent) {
if (event.altKey) {
const eventArgs: IExpansionPanelCancelableEventArgs = { event, panel: this.panel, owner: this.panel, cancel: false };
this.onInteraction.emit(eventArgs);
if (eventArgs.cancel === true) {
return;
}
this.panel.expand(event);
this.onInteraction.emit({ event: event, panel: this.panel });
}
}

/** @hidden @internal */
@HostListener('keydown.Alt.ArrowUp', ['$event'])
public closePanel(event: KeyboardEvent) {
if (event.altKey) {
const eventArgs: IExpansionPanelCancelableEventArgs = { event, panel: this.panel, owner: this.panel, cancel: false };
this.onInteraction.emit(eventArgs);
if (eventArgs.cancel === true) {
return;
}
this.panel.collapse(event);
this.onInteraction.emit({ event: event, panel: this.panel });
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EventEmitter, InjectionToken } from '@angular/core';
import { AnimationReferenceMetadata } from '@angular/animations';
import { IBaseEventArgs } from '../core/utils';
import { CancelableEventArgs, IBaseEventArgs } from '../core/utils';

export interface IgxExpansionPanelBase {
id: string;
Expand All @@ -21,5 +21,11 @@ export const IGX_EXPANSION_PANEL_COMPONENT = new InjectionToken<IgxExpansionPane

export interface IExpansionPanelEventArgs extends IBaseEventArgs {
event: Event;
panel: IgxExpansionPanelBase;
/**
* @deprecated
* To get a reference to the panel, use `owner` instead.
*/
panel?: IgxExpansionPanelBase;
}

export interface IExpansionPanelCancelableEventArgs extends IExpansionPanelEventArgs, CancelableEventArgs {}
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,7 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC
/**
* Emitted when the expansion panel finishes collapsing
* ```typescript
* handleCollapsed(event: {
* panel: IgxExpansionPanelComponent,
* event: Event
* })
* handleCollapsed(event: IExpansionPanelEventArgs)
* ```
* ```html
* <igx-expansion-panel (onCollapsed)="handleCollapsed($event)">
Expand All @@ -136,10 +133,7 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC
/**
* Emitted when the expansion panel finishes expanding
* ```typescript
* handleExpanded(event: {
* panel: IgxExpansionPanelComponent,
* event: Event
* })
* handleExpanded(event: IExpansionPanelEventArgs)
* ```
* ```html
* <igx-expansion-panel (onExpanded)="handleExpanded($event)">
Expand Down Expand Up @@ -229,7 +223,7 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC
}
this.playCloseAnimation(
() => {
this.onCollapsed.emit({ event: evt, panel: this });
this.onCollapsed.emit({ event: evt, panel: this, owner: this });
this.collapsed = true;
}
);
Expand All @@ -253,7 +247,7 @@ export class IgxExpansionPanelComponent implements IgxExpansionPanelBase, AfterC
this.cdr.detectChanges();
this.playOpenAnimation(
() => {
this.onExpanded.emit({ event: evt, panel: this });
this.onExpanded.emit({ event: evt, panel: this, owner: this });
}
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Directive, HostBinding } from '@angular/core';

/**
* @hidden
* @hidden @internal
*/
@Directive({
// tslint:disable-next-line:directive-selector
Expand All @@ -13,7 +13,7 @@ export class IgxExpansionPanelTitleDirective {
}

/**
* @hidden
* @hidden @internal
*/
@Directive({
// tslint:disable-next-line:directive-selector
Expand All @@ -25,7 +25,7 @@ export class IgxExpansionPanelDescriptionDirective {
}

/**
* @hidden
* @hidden @internal
*/
@Directive({
// tslint:disable-next-line:directive-selector
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ describe('igxExpansionPanel', () => {

spyOn(panel.onCollapsed, 'emit');
spyOn(panel.onExpanded, 'emit');
spyOn(header.onInteraction, 'emit');
spyOn(header.onInteraction, 'emit').and.callThrough();
spyOn(panel, 'toggle').and.callThrough();
spyOn(panel, 'expand').and.callThrough();
spyOn(panel, 'collapse').and.callThrough();
Expand All @@ -130,7 +130,9 @@ describe('igxExpansionPanel', () => {
expect(panel.expand).toHaveBeenCalledWith(mockEvent);
expect(panel.collapse).toHaveBeenCalledTimes(0);
expect(panel.onExpanded.emit).toHaveBeenCalledTimes(1);
expect(header.onInteraction.emit).toHaveBeenCalledWith({ event: mockEvent, panel: header.panel });
expect(header.onInteraction.emit).toHaveBeenCalledWith({
event: mockEvent, panel: header.panel, owner: header.panel, cancel: false
});

header.onAction(mockEvent);
tick();
Expand All @@ -153,6 +155,40 @@ describe('igxExpansionPanel', () => {
expect(panel.onCollapsed.emit).toHaveBeenCalledTimes(1);
expect(header.onInteraction.emit).toHaveBeenCalledTimes(2);
expect(panel.onExpanded.emit).toHaveBeenCalledTimes(1);

// cancel event
header.disabled = false;
const headerSub = header.onInteraction.subscribe((event) => {
event.cancel = true;
});

// currently collapsed
expect(panel.collapsed).toBeTruthy();
header.onAction(mockEvent);
tick();
fixture.detectChanges();

// still collapsed, no additional onExpanded calls
expect(panel.collapsed).toBeTruthy();
expect(header.onInteraction.emit).toHaveBeenCalledTimes(3);
expect(panel.onExpanded.emit).toHaveBeenCalledTimes(1);

// expand via API
panel.expand();
tick();
fixture.detectChanges();

// currently expanded
expect(panel.collapsed).toBeFalsy();
header.onAction(mockEvent);
tick();
fixture.detectChanges();

// still expanded, no additional onCollapsed calls
headerSub.unsubscribe();
expect(panel.collapsed).toBeFalsy();
expect(header.onInteraction.emit).toHaveBeenCalledTimes(4);
expect(panel.onCollapsed.emit).toHaveBeenCalledTimes(1);
}));
});

Expand Down Expand Up @@ -447,7 +483,7 @@ describe('igxExpansionPanel', () => {
let timesExpanded = 0;
spyOn(panel.onCollapsed, 'emit').and.callThrough();
spyOn(panel.onExpanded, 'emit').and.callThrough();
spyOn(header.onInteraction, 'emit');
spyOn(header.onInteraction, 'emit').and.callThrough();
verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded);

panelHeader.dispatchEvent(enterEvent);
Expand Down Expand Up @@ -511,6 +547,41 @@ describe('igxExpansionPanel', () => {
timesCollapsed++;
verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded);
expect(header.onInteraction.emit).toHaveBeenCalledTimes(8);

// disabled interaction
const headerSub = header.onInteraction.subscribe((event) => {
event.cancel = true;
});

// currently collapsed
expect(panel.collapsed).toEqual(true);

// cancel openening
panelHeader.dispatchEvent(arrowDownEvent);
fixture.detectChanges();
tick();
// do not iterate timesExpanded
verifyPanelExpansionState(true, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded);
expect(header.onInteraction.emit).toHaveBeenCalledTimes(9);

// open through API
panel.expand();
timesExpanded++;
tick();
fixture.detectChanges();

// currently expanded
expect(panel.collapsed).toEqual(false);

// cancel closing
panelHeader.dispatchEvent(arrowUpEvent);
fixture.detectChanges();
tick();
// do not iterate timesCollapsed
verifyPanelExpansionState(false, panel, panelContainer, panelHeader, button, timesCollapsed, timesExpanded);
expect(header.onInteraction.emit).toHaveBeenCalledTimes(10);

headerSub.unsubscribe();
}));
it('Should change panel expansion when using different methods', fakeAsync(() => {
const fixture: ComponentFixture<IgxExpansionPanelListComponent> = TestBed.createComponent(IgxExpansionPanelListComponent);
Expand Down Expand Up @@ -727,15 +798,28 @@ describe('igxExpansionPanel', () => {
expect(headerButton.children.length).toEqual(2);
expect(iconContainer.firstElementChild.nodeName).toEqual('IGX-ICON');
expect(titleContainer.firstElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-TITLE');
expect(header.iconRef).not.toBe(null);
expect(header.iconRef.nativeElement).toEqual(iconContainer.firstElementChild);

fixture.componentInstance.customIcon = true;
fixture.detectChanges();

expect(iconContainer.firstElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-ICON');
expect(titleContainer.firstElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-TITLE');
expect(header.iconRef).not.toBe(null);
expect(header.iconRef.nativeElement).toEqual(iconContainer.firstElementChild);

fixture.componentInstance.header.iconPosition = ICON_POSITION.NONE;
fixture.detectChanges();
expect(header.iconRef).toEqual(null);

fixture.componentInstance.customIcon = false;
fixture.detectChanges();
expect(header.iconRef).toEqual(null);

fixture.componentInstance.header.iconPosition = ICON_POSITION.LEFT;
fixture.detectChanges();
expect(header.iconRef).not.toBe(null);

expect(iconContainer.firstElementChild.nodeName).toEqual('IGX-ICON');
expect(titleContainer.firstElementChild.nodeName).toEqual('IGX-EXPANSION-PANEL-TITLE');
Expand Down
2 changes: 1 addition & 1 deletion src/app/expansion-panel/expansion-panel-sample.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<div class="game-board">
<igx-expansion-panel [animationSettings]="animationSettings" class="game-board__collapsible" (onCollapsed)="handleCollapsed($event)"
(onExpanded)="handleExpanded($event)" #collapsibleComponent [collapsed]="false">
<igx-expansion-panel-header [iconPosition]="iconPosition" (onInterraction)="onInterraction($event)"
<igx-expansion-panel-header [iconPosition]="iconPosition" (onInteraction)="onInteraction($event)"
[disabled]="false">
<igx-expansion-panel-title>Current Winning Player:</igx-expansion-panel-title>
<igx-expansion-panel-description>{{ getWinningPlayer }}</igx-expansion-panel-description>
Expand Down
Loading

0 comments on commit 4075879

Please sign in to comment.