Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(expansion-panel): make header interaction cancelable - master #8268

Merged
merged 4 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
3 changes: 2 additions & 1 deletion projects/igniteui-angular/src/lib/expansion-panel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 | `{ event: Event, panel: IgxExpansionPanelComponent, cancel: boolean }` |
wnvko marked this conversation as resolved.
Show resolved Hide resolved

## 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,25 @@ export class IgxExpansionPanelHeaderComponent {
*/
public id = '';

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

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

/**
* Returns a reference to the `igx-expansion-panel-icon` element;
* If `iconPosition` is `NONE` - return null;
*/
public get iconRef(): ElementRef {
const defaultRef = this.defaultIconRef ? this.defaultIconRef.el : null;
wnvko marked this conversation as resolved.
Show resolved Hide resolved
const customRef = this.customIconRef ? this.customIconRef : null;
wnvko marked this conversation as resolved.
Show resolved Hide resolved
const renderedTemplate = this.iconTemplate ? customRef : defaultRef;
wnvko marked this conversation as resolved.
Show resolved Hide resolved
return this.iconPosition !== ICON_POSITION.NONE ? renderedTemplate : null;
}

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

/**
* @hidden
Expand Down Expand Up @@ -185,7 +206,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 +219,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 @@ -229,7 +229,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 });
wnvko marked this conversation as resolved.
Show resolved Hide resolved
this.collapsed = true;
}
);
Expand All @@ -253,7 +253,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 });
wnvko marked this conversation as resolved.
Show resolved Hide resolved
}
);
}
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