Skip to content

Commit

Permalink
Merge branch 'ddincheva/gridSelection' of https://github.com/IgniteUI…
Browse files Browse the repository at this point in the history
…/igniteui-angular into bpenkov/row-selection-templating
  • Loading branch information
jackofdiamond5 committed Aug 20, 2019
2 parents 75ad7a7 + bcc569f commit 37a7e66
Show file tree
Hide file tree
Showing 29 changed files with 1,454 additions and 567 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ All notable changes for each version of this project will be documented in this
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
- `uniqueColumnValuesStrategy` input is added. This property provides a callback for loading unique column values on demand. If this property is provided, the unique values it generates will be used by the Excel Style Filtering (instead of using the unique values from the data that is bound to the grid).
- `igxExcelStyleLoading` directive is added, which can be used to provide a custom loading template for the Excel Style Filtering. If this property is not provided, a default loading template will be used instead.
- introduced new propoerties `cellSelection` and `rowSelection` which accepts GridSelection mode enumeration. Grid selection mode could be none, single or multiple. Also `hideRowSelectors` property is added, which allows you to show and hide row selectors when row selection is enabled.
### General
- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`
- Removed deprecated event `OnFocusChange`
- **Breaking Change** `igxExcelStyleSortingTemplate` directive is renamed to `igxExcelStyleSorting`.
- **Breaking Change** `igxExcelStyleMovingTemplate` directive is renamed to `igxExcelStyleMoving`.
- **Breaking Change** `igxExcelStyleHidingTemplate` directive is renamed to `igxExcelStyleHiding`.
- **Breaking Change** `igxExcelStylePinningTemplate` directive is renamed to `igxExcelStylePinning`.
## 8.1.4
- `IgxDialog` new @Input `positionSettings` is now available. It provides the ability to get/set both position and animation settings of the Dialog component.

## 8.1.3
- `IgxCombo`
Expand Down
52 changes: 32 additions & 20 deletions projects/igniteui-angular/src/lib/core/grid-selection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ export class IgxGridSelectionService {
_ranges: Set<string> = new Set<string>();
_selectionRange: Range;
rowSelection: Set<any> = new Set<any>();
allRowsSelected: boolean;
private allRowsSelected: boolean;

/**
* Returns the current selected ranges in the grid from both
Expand All @@ -228,7 +228,7 @@ export class IgxGridSelectionService {
const ranges = Array.from(this._ranges).map(range => JSON.parse(range));

// No ranges but we have a focused cell -> add it
if (!ranges.length && this.activeElement) {
if (!ranges.length && this.activeElement && this.grid.cellSelection !== 'none') {
ranges.push(this.generateRange(this.activeElement));
}

Expand Down Expand Up @@ -305,7 +305,7 @@ export class IgxGridSelectionService {
}

selected(node: ISelectionNode): boolean {
return this.isActiveNode(node) || this.isInMap(node);
return (this.isActiveNode(node) && this.grid.cellSelection !== 'none' ) || this.isInMap(node);
}

isActiveNode(node: ISelectionNode, mrl = false): boolean {
Expand Down Expand Up @@ -548,25 +548,24 @@ export class IgxGridSelectionService {
return this.rowSelection.size ? Array.from(this.rowSelection.keys()) : [];
}

clearRowSelection(onlyFiltered = true, shouldEmitEvent = true, event?) {
clearRowSelection(event?) {
this.allRowsSelected = false;
onlyFiltered = this.isFilteringApplied() && onlyFiltered;
const removedRec = this.isFilteringApplied() ?
this.getRowIDs(this.allData).filter(rID => this.isRowSelected(rID)) : this.getSelectedRows();
const newSelection = this.isFilteringApplied() ? this.getSelectedRows().filter(x => !removedRec.includes(x)) : [];
if (this.emitRowSelectionEvent(newSelection, [], removedRec, event)) { return; }

const removedRec = onlyFiltered ? this.getRowIDs(this.allData).filter(rID => this.isRowSelected(rID)) : this.getSelectedRows();
const newSelection = this.getSelectedRows().filter(x => !removedRec.includes(x));
if (shouldEmitEvent && this.emitRowSelectionEvent(newSelection, [], removedRec, event)) { return; }

onlyFiltered ? this.deselectRowsWithNoEvent(removedRec) : this.rowSelection.clear();
this.isFilteringApplied() ? this.deselectRowsWithNoEvent(removedRec) : this.rowSelection.clear();
}

selectAllRows(onlyFiltered = true, shouldEmitEvent = true, event?) {
selectAllRows(event?) {
this.allRowsSelected = true;
const allData = onlyFiltered ? this.allData : this.grid.gridAPI.get_all_data();
const allRowIDs = this.getRowIDs(allData);
const addedRows = allRowIDs.filter((rID) => !this.isRowSelected(rID));

if (shouldEmitEvent && this.emitRowSelectionEvent(allRowIDs, addedRows, [], event)) { return; }
this.selectRows(addedRows);
const allRowIDs = this.getRowIDs(this.allData);
const addedRows = this.rowSelection.size ? allRowIDs.filter((rID) => !this.isRowSelected(rID)) : allRowIDs;

if (this.emitRowSelectionEvent(allRowIDs, addedRows, [], event)) { return; }
this.selectRowsWithNoEvent(addedRows);
}

selectRowbyID(rowID, clearPrevSelection?, event?) {
Expand All @@ -592,7 +591,7 @@ export class IgxGridSelectionService {
}
}

selectRows(rowIDs: any[], clearPrevSelection?) {
selectRowsWithNoEvent(rowIDs: any[], clearPrevSelection?) {
if (clearPrevSelection) { this.rowSelection.clear(); }
rowIDs.forEach(rowID => { this.rowSelection.add(rowID); });
this.allRowsSelected = undefined;
Expand Down Expand Up @@ -642,15 +641,20 @@ export class IgxGridSelectionService {

public emitRowSelectionEvent(newSelection, added, removed, event?): boolean {
const currSelection = this.getSelectedRows();
if (currSelection.length === newSelection.length &&
new Set(currSelection.concat(newSelection)).size === currSelection.length) { return; }
if (this.areEquelCollections(currSelection, newSelection)) { return; }
const copy = Array.from(newSelection);
const args = {oldSelection: currSelection, newSelection: newSelection,
added: added, removed: removed, event: event, cancel: false};
this.grid.onRowSelectionChange.emit(args);
if (args.cancel && event.checkbox) { event.checkbox.checked = !event.checkbox.checked; }
if (!this.areEquelCollections(copy, newSelection)) {
this.selectRowsWithNoEvent(newSelection, true);
return true;
}
return args.cancel;
}


public getRowDataByID(rowID) {
if (!this.grid.primaryKey) { return rowID; }
const rowIndex = this.getRowIDs(this.grid.gridAPI.get_all_data()).indexOf(rowID);
Expand All @@ -661,13 +665,21 @@ export class IgxGridSelectionService {
return this.grid.primaryKey && data.length ? data.map(rec => rec[this.grid.primaryKey]) : data;
}

private get allData() {
public clearHeaderCBState() {
this.allRowsSelected = undefined;
}

public get allData() {
const gridAPI = this.grid.gridAPI;
const allData = this.isFilteringApplied() || this.grid.sortingExpressions.length ?
this.grid.filteredSortedData : gridAPI.get_all_data();
return allData.filter(rData => !this.isRowDeleted(gridAPI.get_row_id(rData)));
}

private areEquelCollections(first, second) {
return first.length !== second.length || new Set(first.concat(second)).size !== first.length ? false : true;
}

private isFilteringApplied() {
return this.grid.filteringExpressionsTree.filteringOperands.length > 0;
}
Expand Down
20 changes: 20 additions & 0 deletions projects/igniteui-angular/src/lib/dialog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,23 @@ or
<div igxDialogActions>BUTTONS</div>
</igx-dialog>
```

You can now set set the position and animation settings used by the dialog by using `positionSettings` @Input

```typescript
import { slideInLeft, slideOutRight } from 'igniteui-angular';
...
@ViewChild('alert', { static: true }) public alert: IgxDialogComponent;
public newPositionSettings: PositionSettings = {
openAnimation: useAnimation(slideInTop, { params: { duration: '2000ms' } }),
closeAnimation: useAnimation(slideOutBottom, { params: { duration: '2000ms'} }),
horizontalDirection: HorizontalAlignment.Left,
verticalDirection: VerticalAlignment.Middle,
horizontalStartPoint: HorizontalAlignment.Left,
verticalStartPoint: VerticalAlignment.Middle,
minSize: { height: 100, width: 100 }
};

this.alert.positionSettings = this.newPositionSettings;
```

111 changes: 108 additions & 3 deletions projects/igniteui-angular/src/lib/dialog/dialog.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Component, DebugElement, ElementRef, ViewChild } from '@angular/core';
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { Component, ViewChild } from '@angular/core';
import { async, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
import { UIInteractions, wait } from '../test-utils/ui-interactions.spec';
import { IDialogEventArgs, IgxDialogComponent, IgxDialogModule } from './dialog.component';
import { configureTestSuite } from '../test-utils/configure-suite';
import { PositionSettings, slideInTop, slideOutBottom, HorizontalAlignment, VerticalAlignment } from 'igniteui-angular';
import { useAnimation } from '@angular/animations';

const OVERLAY_MAIN_CLASS = 'igx-overlay';
const OVERLAY_WRAPPER_CLASS = `${OVERLAY_MAIN_CLASS}__wrapper`;
const OVERLAY_MODAL_WRAPPER_CLASS = `${OVERLAY_WRAPPER_CLASS}--modal`;
const CLASS_OVERLAY_CONTENT_MODAL = `${OVERLAY_MAIN_CLASS}__content--modal`;

describe('Dialog', () => {
configureTestSuite();
Expand All @@ -21,7 +24,8 @@ describe('Dialog', () => {
NestedDialogsComponent,
CustomTemplates1DialogComponent,
CustomTemplates2DialogComponent,
DialogSampleComponent
DialogSampleComponent,
PositionSettingsDialogComponent
],
imports: [BrowserAnimationsModule, NoopAnimationsModule, IgxDialogModule]
}).compileComponents();
Expand Down Expand Up @@ -322,6 +326,78 @@ describe('Dialog', () => {
expect(dialog.isOpen).toEqual(false);
}));

describe('Position settings', () => {
let fix;
let dialog;
let detect;

beforeEach( fakeAsync(() => {
fix = TestBed.createComponent(PositionSettingsDialogComponent);
fix.detectChanges();
dialog = fix.componentInstance.dialog;
detect = () => dialog.cdr.detectChanges();
}));

it('Define different position settings ', (async() => {
const currentElement = fix.componentInstance;
dialog.open();
fix.detectChanges();
await wait(16);

expect(dialog.isOpen).toEqual(true);
const firstContentRect = document.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0].getBoundingClientRect();
const middleDialogPosition = document.documentElement.offsetHeight / 2 - firstContentRect.height / 2;
expect(firstContentRect.left).toEqual(0, 'OffsetLeft position check');
expect(firstContentRect.top).toBeGreaterThanOrEqual(middleDialogPosition - 2, 'OffsetTop position check');
expect(firstContentRect.top).toBeLessThanOrEqual(middleDialogPosition + 2, 'OffsetTop position check');

dialog.close();
fix.detectChanges();
await wait(16);

expect(dialog.isOpen).toEqual(false);
dialog.positionSettings = currentElement.newPositionSettings;
fix.detectChanges();
await wait(16);

dialog.open();
fix.detectChanges();
await wait(16);

expect(dialog.isOpen).toEqual(true);
const secondContentRect = document.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0].getBoundingClientRect();
const topDialogPosition = document.documentElement.offsetWidth / 2 - secondContentRect.width / 2;
expect(secondContentRect.top).toEqual(0, 'OffsetTop position check');
expect(secondContentRect.left).toBeGreaterThanOrEqual(topDialogPosition - 2, 'OffsetLeft position check');
expect(secondContentRect.left).toBeLessThanOrEqual(topDialogPosition + 2, 'OffsetLeft position check');

dialog.close();
fix.detectChanges();
await wait(16);

expect(dialog.isOpen).toEqual(false);
}));

it('Set animation settings', (async() => {
const currentElement = fix.componentInstance;

// Check initial animation settings
expect(dialog.positionSettings.openAnimation.animation.type).toEqual(8, 'Animation type is set');
expect(dialog.positionSettings.openAnimation.options.params.duration).toEqual('200ms', 'Animation duration is set to 200ms');

expect(dialog.positionSettings.closeAnimation.animation.type).toEqual(8, 'Animation type is set');
expect(dialog.positionSettings.closeAnimation.options.params.duration).toEqual('200ms', 'Animation duration is set to 200ms');

dialog.positionSettings = currentElement.animationSettings;
fix.detectChanges();
await wait(16);

// Check the new animation settings
expect(dialog.positionSettings.openAnimation.options.params.duration).toEqual('800ms', 'Animation duration is set to 800ms');
expect(dialog.positionSettings.closeAnimation.options.params.duration).toEqual('700ms', 'Animation duration is set to 700ms');
}));
});

function dispatchEvent(element: HTMLElement, eventType: string) {
const event = new Event(eventType);
element.dispatchEvent(event);
Expand Down Expand Up @@ -436,3 +512,32 @@ class CustomTemplates1DialogComponent {
class CustomTemplates2DialogComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
}


@Component({
template: `<igx-dialog #dialog title="Notification" message="Your email has been sent successfully!" leftButtonLabel="OK"
[positionSettings]="positionSettings" >
</igx-dialog>` })
class PositionSettingsDialogComponent {
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;

public positionSettings: PositionSettings = {
horizontalDirection: HorizontalAlignment.Left,
verticalDirection: VerticalAlignment.Middle,
horizontalStartPoint: HorizontalAlignment.Left,
verticalStartPoint: VerticalAlignment.Middle,
openAnimation: useAnimation(slideInTop, { params: { duration: '200ms' } }),
closeAnimation: useAnimation(slideOutBottom, { params: { duration: '200ms'} })
};

public newPositionSettings: PositionSettings = {
horizontalDirection: HorizontalAlignment.Center,
verticalDirection: VerticalAlignment.Top
};

public animationSettings: PositionSettings = {
openAnimation: useAnimation(slideInTop, { params: { duration: '800ms' } }),
closeAnimation: useAnimation(slideOutBottom, { params: { duration: '700ms'} })
};

}
39 changes: 37 additions & 2 deletions projects/igniteui-angular/src/lib/dialog/dialog.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,41 @@ export class IgxDialogComponent implements IToggleView, OnInit, OnDestroy, After
this._closeOnOutsideSelect = val;
}

/**
* Get the position and animation settings used by the dialog.
* ```typescript
* @ViewChild('alert', { static: true }) public alert: IgxDialogComponent;
* let currentPosition: PositionSettings = this.alert.positionSettings
* ```
*/
@Input()
public get positionSettings(): PositionSettings {
return this._positionSettings;
}

/**
* Set the position and animation settings used by the dialog.
* ```typescript
* import { slideInLeft, slideOutRight } from 'igniteui-angular';
* ...
* @ViewChild('alert', { static: true }) public alert: IgxDialogComponent;
* public newPositionSettings: PositionSettings = {
* openAnimation: useAnimation(slideInTop, { params: { duration: '2000ms' } }),
* closeAnimation: useAnimation(slideOutBottom, { params: { duration: '2000ms'} }),
* horizontalDirection: HorizontalAlignment.Left,
* verticalDirection: VerticalAlignment.Middle,
* horizontalStartPoint: HorizontalAlignment.Left,
* verticalStartPoint: VerticalAlignment.Middle,
* minSize: { height: 100, width: 100 }
* };
* this.alert.positionSettings = this.newPositionSettings;
* ```
*/
public set positionSettings(settings: PositionSettings) {
this._positionSettings = settings;
this._overlayDefaultSettings.positionStrategy = new GlobalPositionStrategy(this._positionSettings);
}

/**
* An event that is emitted when the dialog is opened.
*```html
Expand Down Expand Up @@ -258,7 +293,7 @@ export class IgxDialogComponent implements IToggleView, OnInit, OnDestroy, After
@Output()
public onRightButtonSelect = new EventEmitter<IDialogEventArgs>();

private _animaitonSettings: PositionSettings = {
private _positionSettings: PositionSettings = {
openAnimation: useAnimation(slideInBottom, { params: { fromPosition: 'translateY(100%)' } }),
closeAnimation: useAnimation(slideOutTop, { params: { toPosition: 'translateY(-100%)' } })
};
Expand Down Expand Up @@ -365,7 +400,7 @@ export class IgxDialogComponent implements IToggleView, OnInit, OnDestroy, After
this._titleId = IgxDialogComponent.NEXT_ID++ + '_title';

this._overlayDefaultSettings = {
positionStrategy: new GlobalPositionStrategy(this._animaitonSettings),
positionStrategy: new GlobalPositionStrategy(this._positionSettings),
scrollStrategy: new NoOpScrollStrategy(),
modal: this.isModal,
closeOnOutsideClick: this.closeOnOutsideSelect
Expand Down
Loading

0 comments on commit 37a7e66

Please sign in to comment.