From 9d5552535e37154e1fa8b46f88030559a1292fc1 Mon Sep 17 00:00:00 2001 From: ViktorSlavov Date: Tue, 30 Jul 2019 16:06:07 +0300 Subject: [PATCH 1/5] feat(combo): enhance combo selection event, #5523 --- .../src/lib/combo/combo.component.ts | 18 ++++++++++++------ .../igniteui-angular/src/lib/core/utils.ts | 8 ++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/combo/combo.component.ts b/projects/igniteui-angular/src/lib/combo/combo.component.ts index 2a2fe28424e..f36fecf7b58 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.component.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.component.ts @@ -17,7 +17,7 @@ import { import { FormsModule, ReactiveFormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms'; import { IgxCheckboxModule } from '../checkbox/checkbox.component'; import { IgxSelectionAPIService } from '../core/selection'; -import { cloneArray, CancelableEventArgs, CancelableBrowserEventArgs } from '../core/utils'; +import { cloneArray, CancelableEventArgs, CancelableBrowserEventArgs, diffInSets } from '../core/utils'; import { IgxStringFilteringOperand, IgxBooleanFilteringOperand } from '../data-operations/filtering-condition'; import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface'; import { SortingDirection, ISortingExpression } from '../data-operations/sorting-expression.interface'; @@ -86,6 +86,8 @@ export enum IgxComboState { export interface IComboSelectionChangeEventArgs extends CancelableEventArgs { oldSelection: any[]; newSelection: any[]; + added: any[]; + removed: any[]; event?: Event; } @@ -327,7 +329,7 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas * * ... * - *
+ *
* There are no items to display *
* @@ -703,7 +705,7 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas } /** - * Combo value data source propery. + * Combo value data source property. * * ```typescript * // get @@ -724,7 +726,7 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas } /** - * Combo text data source propery. + * Combo text data source property. * * ```typescript * // get @@ -737,7 +739,7 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas * * ```html * - * + * * ``` */ get displayKey() { @@ -1472,7 +1474,7 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas /** * Selects/Deselects an item using it's valueKey value * @param itemID the valueKey of the specified item - * @param select If the item should be selected (true) or deselcted (false) + * @param select If the item should be selected (true) or deselected (false) * * ```typescript * items: { field: string, region: string}[] = data; @@ -1496,11 +1498,15 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas } protected setSelection(newSelection: Set, event?: Event): void { + const removed = Array.from(diffInSets(this.selection.get(this.id), newSelection)); + const added = Array.from(diffInSets(newSelection, this.selection.get(this.id))); const oldSelectionEmit = Array.from(this.selection.get(this.id) || []); const newSelectionEmit = Array.from(newSelection || []); const args: IComboSelectionChangeEventArgs = { newSelection: newSelectionEmit, oldSelection: oldSelectionEmit, + added, + removed, event, cancel: false }; diff --git a/projects/igniteui-angular/src/lib/core/utils.ts b/projects/igniteui-angular/src/lib/core/utils.ts index 1481ae973af..7e335a5697c 100644 --- a/projects/igniteui-angular/src/lib/core/utils.ts +++ b/projects/igniteui-angular/src/lib/core/utils.ts @@ -13,6 +13,14 @@ export function cloneArray(array: any[], deep?: boolean) { return arr; } +/** + * When called with sets A & B, returns A - B (as set); + * @hidden + */ +export function diffInSets(set1: Set, set2: Set): Set { + return new Set(Array.from(set1).filter(entry => !set2.has(entry))); +} + /** * Doesn't clone leaf items * @hidden From f22404c757daf17de30a0f98a0b4c068349e82e4 Mon Sep 17 00:00:00 2001 From: ViktorSlavov Date: Wed, 31 Jul 2019 11:39:26 +0300 Subject: [PATCH 2/5] feat(combo): add tests for selection events, #5523 --- .../src/lib/combo/combo.component.spec.ts | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/projects/igniteui-angular/src/lib/combo/combo.component.spec.ts b/projects/igniteui-angular/src/lib/combo/combo.component.spec.ts index 56960f41449..dcc8a7eec9f 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.component.spec.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.component.spec.ts @@ -1576,6 +1576,64 @@ describe('igxCombo', () => { // onSelectionChange fires and overrides the selection to be []; expect(combo.selectedItems()).toEqual([]); })); + + it('Should properly emit added and removed values in change event - single values', () => { + const fixture = TestBed.createComponent(IgxComboSampleComponent); + fixture.detectChanges(); + const combo = fixture.componentInstance.combo; + const selectionSpy = spyOn(fixture.componentInstance, 'onSelectionChange'); + const expectedResults = { + newSelection: [combo.data[0]], + oldSelection: [], + added: [combo.data[0]], + removed: [], + event: undefined, + cancel: false + }; + combo.selectItems([combo.data[0]]); + expect(selectionSpy).toHaveBeenCalledWith(expectedResults); + Object.assign(expectedResults, { + newSelection: [], + oldSelection: [combo.data[0]], + added: [], + removed: [combo.data[0]] + }); + combo.deselectItems([combo.data[0]]); + expect(selectionSpy).toHaveBeenCalledWith(expectedResults); + }); + + it('Should properly emit added and removed values in change event - multiple values', () => { + const fixture = TestBed.createComponent(IgxComboSampleComponent); + fixture.detectChanges(); + const combo = fixture.componentInstance.combo; + const selectionSpy = spyOn(fixture.componentInstance, 'onSelectionChange'); + const expectedResults = { + newSelection: [combo.data[0], combo.data[1], combo.data[2]], + oldSelection: [], + added: [combo.data[0], combo.data[1], combo.data[2]], + removed: [], + event: undefined, + cancel: false + }; + combo.selectItems([combo.data[0], combo.data[1], combo.data[2]]); + expect(selectionSpy).toHaveBeenCalledWith(expectedResults); + combo.deselectItems([combo.data[0]]); + Object.assign(expectedResults, { + newSelection: [combo.data[1], combo.data[2]], + oldSelection: [combo.data[0], combo.data[1], combo.data[2]], + added: [], + removed: [combo.data[0]] + }); + expect(selectionSpy).toHaveBeenCalledWith(expectedResults); + Object.assign(expectedResults, { + newSelection: [combo.data[4], combo.data[5], combo.data[6]], + oldSelection: [combo.data[1], combo.data[2]], + added: [combo.data[4], combo.data[5], combo.data[6]], + removed: [combo.data[1], combo.data[2]] + }); + combo.selectItems([combo.data[4], combo.data[5], combo.data[6]], true); + expect(selectionSpy).toHaveBeenCalledWith(expectedResults); + }); }); describe('Rendering tests: ', () => { From f364093cc5a599c7b9697d10f4a0a124f6e6326c Mon Sep 17 00:00:00 2001 From: ViktorSlavov Date: Wed, 31 Jul 2019 14:17:37 +0300 Subject: [PATCH 3/5] test(combo): fix failing tests, #5523 --- .../src/lib/combo/combo.component.spec.ts | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/projects/igniteui-angular/src/lib/combo/combo.component.spec.ts b/projects/igniteui-angular/src/lib/combo/combo.component.spec.ts index dcc8a7eec9f..f5699784a5d 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.component.spec.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.component.spec.ts @@ -1015,6 +1015,8 @@ describe('igxCombo', () => { expect(combo.onSelectionChange.emit).toHaveBeenCalledWith({ oldSelection: [], newSelection: [targetItem.itemID], + added: [targetItem.itemID], + removed: [], event: undefined, cancel: false }); @@ -1027,6 +1029,8 @@ describe('igxCombo', () => { expect(combo.onSelectionChange.emit).toHaveBeenCalledWith({ oldSelection: [targetItem.itemID], newSelection: [], + added: [], + removed: [targetItem.itemID], event: undefined, cancel: false }); @@ -1049,6 +1053,8 @@ describe('igxCombo', () => { expect(combo.onSelectionChange.emit).toHaveBeenCalledWith({ oldSelection: oldSelection, newSelection: newSelection, + added: [combo.data[1], combo.data[5], combo.data[6]], + removed: [], event: undefined, cancel: false }); @@ -1063,6 +1069,8 @@ describe('igxCombo', () => { expect(combo.onSelectionChange.emit).toHaveBeenCalledWith({ oldSelection: oldSelection, newSelection: newSelection, + removed: [], + added: [combo.data[3]], event: undefined, cancel: false }); @@ -1076,6 +1084,8 @@ describe('igxCombo', () => { expect(combo.onSelectionChange.emit).toHaveBeenCalledWith({ oldSelection: oldSelection, newSelection: newSelection, + removed: oldSelection, + added: [combo.data[0]], event: undefined, cancel: false }); @@ -1091,6 +1101,8 @@ describe('igxCombo', () => { expect(combo.onSelectionChange.emit).toHaveBeenCalledWith({ oldSelection: oldSelection, newSelection: newSelection, + removed: [combo.data[0]], + added: [], event: undefined, cancel: false }); @@ -1410,6 +1422,8 @@ describe('igxCombo', () => { const eventParams = { oldSelection: [], newSelection: [], + added: [], + removed: [], event: mockEvent, cancel: false }; @@ -1427,29 +1441,23 @@ describe('igxCombo', () => { expect(combo.onSelectionChange.emit).toHaveBeenCalledWith(eventParams); }; - eventParams.newSelection.push(dropdown.items[3].value); + eventParams.newSelection = [dropdown.items[3].value]; + eventParams.added = [dropdown.items[3].value]; verifyOnSelectionChangeEventIsFired(3); timesFired++; - eventParams.oldSelection.push(dropdown.items[3].value); - eventParams.newSelection.push(dropdown.items[7].value); + eventParams.oldSelection = [dropdown.items[3].value]; + eventParams.newSelection = [dropdown.items[3].value, dropdown.items[7].value]; + eventParams.added = [dropdown.items[7].value]; verifyOnSelectionChangeEventIsFired(7); timesFired++; - eventParams.oldSelection.push(dropdown.items[7].value); - eventParams.newSelection.push(dropdown.items[1].value); - verifyOnSelectionChangeEventIsFired(1); - timesFired++; - // Deselecting an item - eventParams.oldSelection.push(dropdown.items[1].value); - eventParams.newSelection = eventParams.newSelection.filter(item => item !== dropdown.items[7].value); + eventParams.oldSelection = [dropdown.items[3].value, dropdown.items[7].value]; + eventParams.newSelection = [dropdown.items[3].value]; + eventParams.added = []; + eventParams.removed = [dropdown.items[7].value]; verifyOnSelectionChangeEventIsFired(7); - timesFired++; - - eventParams.oldSelection = eventParams.oldSelection.filter(item => item !== dropdown.items[7].value); - eventParams.newSelection = eventParams.newSelection.filter(item => item !== dropdown.items[1].value); - verifyOnSelectionChangeEventIsFired(1); })); it('Should be able to select item when in grouped state', fakeAsync(() => { const fixture = TestBed.createComponent(IgxComboSampleComponent); From 86f2a7a6fe47bebdb5d7bc2bc3008305cd893d72 Mon Sep 17 00:00:00 2001 From: ViktorSlavov Date: Wed, 31 Jul 2019 15:04:26 +0300 Subject: [PATCH 4/5] docs(combo): update docs for combo IComboSelectionChangeEvents, #5523 # Conflicts: # CHANGELOG.md --- CHANGELOG.md | 42 +++++++++++++++++++ .../igniteui-angular/src/lib/combo/README.md | 20 ++++----- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0c86a271d7..65da84b51ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,53 @@ All notable changes for each version of this project will be documented in this file. +## 8.2.0 +- `IgxCombo` + - Combo `onSelectionChange` events now emits the item(s) that were added to or removed from the collection: + ```html + + ``` + ```typescript + export class Example { + ... + handleChange(event: { + newSelection: any[], + oldSelection: any[], + added: any[], // the items added to the selection in this change + removed: any[], // the items removed for the selection in this change + ... + }) { + console.log("Items added: ", [...event.added]); + console.log("Items removed: ", [...event.removed]); + } + } + ``` + ## 8.1.2 ### New Features - `IgxDatePicker` - `valueChange` event is added. +- `IgxCombo` + - Combo `onSelectionChange` events now emits the item(s) that were added to or removed from the collection: + ```html + + ``` + ```typescript + export class Example { + ... + handleChange(event: { + newSelection: any[], + oldSelection: any[], + added: any[], // the items added to the selection in this change + removed: any[], // the items removed for the selection in this change + ... + }) { + console.log("Items added: ", [...event.added]); + console.log("Items removed: ", [...event.removed]); + } + } + ``` ## 8.1.0 diff --git a/projects/igniteui-angular/src/lib/combo/README.md b/projects/igniteui-angular/src/lib/combo/README.md index 5f562ede8ba..d5def85af3a 100644 --- a/projects/igniteui-angular/src/lib/combo/README.md +++ b/projects/igniteui-angular/src/lib/combo/README.md @@ -276,14 +276,14 @@ Setting `[displayDensity]` affects the control's items' and inputs' css properti | Name | Description | Cancelable | Parameters | |------------------ |-------------------------------------------------------------------------|------------- |-----------------------------------------| -| `onSelectionChange` | Emitted when item selection is changing, before the selection completes | true | { oldSelection: `Array`, newSelection: `Array`, event: Event } | +| `onSelectionChange` | Emitted when item selection is changing, before the selection completes | true | { oldSelection: `any[]`, newSelection: `any[]`, event?: `Event`, added: `any[]`, removed: `any[]`, cancel: `boolean` } | | `onSearchInput` | Emitted when an the search input's input event is triggered | false | { searchValue: `string` } | -| `onAddition` | Emitted when an item is being added to the data collection | false | { oldCollection: `Array`, addedItem: ``, newCollection: `Array` }| -| `onDataPreLoad` | Emitted when new chunk of data is loaded from the virtualization | false | { event: Event } | -| `onOpening` | Emitted before the dropdown is opened | false | { event: Event } | -| `onOpened` | Emitted after the dropdown is opened | false | { event: Event } | -| `onClosing` | Emitted before the dropdown is closed | false | { event: Event } | -| `onClosed` | Emitted after the dropdown is closed | false | { event: Event } | +| `onAddition` | Emitted when an item is being added to the data collection | false | { oldCollection: `any[]`, addedItem: ``, newCollection: `any[]` }| +| `onDataPreLoad` | Emitted when new chunk of data is loaded from the virtualization | false | { event: `Event` } | +| `onOpening` | Emitted before the dropdown is opened | false | { event: `Event` } | +| `onOpened` | Emitted after the dropdown is opened | false | { event: `Event` } | +| `onClosing` | Emitted before the dropdown is closed | false | { event: `Event` } | +| `onClosed` | Emitted after the dropdown is closed | false | { event: `Event` } | ### Methods @@ -292,9 +292,9 @@ Setting `[displayDensity]` affects the control's items' and inputs' css properti | `open` | Opens drop down | `void` | `None` | | `close` | Closes drop down | `void` | `None` | | `toggle` | Toggles drop down | `void` | `None` | -| `selectedItems` | Get current selection state | `Array` | `None` | -| `selectItems` | Select defined items | `void` | items: `Array`, clearCurrentSelection: `boolean` | -| `deselectItems` | Deselect defined items | `void` | items: `Array` | +| `selectedItems` | Get current selection state | `any[]` | `None` | +| `selectItems` | Select defined items | `void` | items: `any[]`, clearCurrentSelection: `boolean` | +| `deselectItems` | Deselect defined items | `void` | items: `any[]` | | `selectAllItems` | Select all (filtered) items | `void` | ignoreFilter?: `boolean` - if `true` selects **all** values | | `deselectAllItems` | Deselect (filtered) all items | `void` | ignoreFilter?: `boolean` - if `true` deselects **all** values | | `setSelectedItem` | Toggles (select/deselect) an item by key | `void` | itemID: any, select = true, event?: Event | From 919f31ef552dddefa9e65754ceb95821cbf87b6b Mon Sep 17 00:00:00 2001 From: ViktorSlavov Date: Thu, 8 Aug 2019 15:56:03 +0300 Subject: [PATCH 5/5] chore(combo): move diffInSets definition to combo, update README.md --- CHANGELOG.md | 36 +++---------------- .../igniteui-angular/src/lib/combo/README.md | 2 +- .../src/lib/combo/combo.component.ts | 26 ++++++++++++-- .../igniteui-angular/src/lib/core/utils.ts | 8 ----- 4 files changed, 29 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65da84b51ac..c562562268d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes for each version of this project will be documented in this file. -## 8.2.0 +## 8.1.3 - `IgxCombo` - Combo `onSelectionChange` events now emits the item(s) that were added to or removed from the collection: ```html @@ -11,44 +11,18 @@ All notable changes for each version of this project will be documented in this ```typescript export class Example { ... - handleChange(event: { - newSelection: any[], - oldSelection: any[], - added: any[], // the items added to the selection in this change - removed: any[], // the items removed for the selection in this change - ... - }) { - console.log("Items added: ", [...event.added]); - console.log("Items removed: ", [...event.removed]); + handleChange(event: IComboSelectionChangeEventArgs) { + console.log("Items added: ", [...event.added]); // the items added to the selection in this change + console.log("Items removed: ", [...event.removed]); // the items removed from the selection in this change } } ``` - + ## 8.1.2 ### New Features - `IgxDatePicker` - `valueChange` event is added. -- `IgxCombo` - - Combo `onSelectionChange` events now emits the item(s) that were added to or removed from the collection: - ```html - - ``` - ```typescript - export class Example { - ... - handleChange(event: { - newSelection: any[], - oldSelection: any[], - added: any[], // the items added to the selection in this change - removed: any[], // the items removed for the selection in this change - ... - }) { - console.log("Items added: ", [...event.added]); - console.log("Items removed: ", [...event.removed]); - } - } - ``` ## 8.1.0 diff --git a/projects/igniteui-angular/src/lib/combo/README.md b/projects/igniteui-angular/src/lib/combo/README.md index d5def85af3a..7f9ac79b542 100644 --- a/projects/igniteui-angular/src/lib/combo/README.md +++ b/projects/igniteui-angular/src/lib/combo/README.md @@ -276,7 +276,7 @@ Setting `[displayDensity]` affects the control's items' and inputs' css properti | Name | Description | Cancelable | Parameters | |------------------ |-------------------------------------------------------------------------|------------- |-----------------------------------------| -| `onSelectionChange` | Emitted when item selection is changing, before the selection completes | true | { oldSelection: `any[]`, newSelection: `any[]`, event?: `Event`, added: `any[]`, removed: `any[]`, cancel: `boolean` } | +| `onSelectionChange` | Emitted when item selection is changing, before the selection completes | true | [`IComboSelectionChangeEventArgs`](https://www.infragistics.com/products/ignite-ui-angular/docs/typescript/latest/interfaces/icomboselectionchangeeventargs.html) | | `onSearchInput` | Emitted when an the search input's input event is triggered | false | { searchValue: `string` } | | `onAddition` | Emitted when an item is being added to the data collection | false | { oldCollection: `any[]`, addedItem: ``, newCollection: `any[]` }| | `onDataPreLoad` | Emitted when new chunk of data is loaded from the virtualization | false | { event: `Event` } | diff --git a/projects/igniteui-angular/src/lib/combo/combo.component.ts b/projects/igniteui-angular/src/lib/combo/combo.component.ts index f36fecf7b58..26936e1ad04 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.component.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.component.ts @@ -17,7 +17,7 @@ import { import { FormsModule, ReactiveFormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms'; import { IgxCheckboxModule } from '../checkbox/checkbox.component'; import { IgxSelectionAPIService } from '../core/selection'; -import { cloneArray, CancelableEventArgs, CancelableBrowserEventArgs, diffInSets } from '../core/utils'; +import { cloneArray, CancelableEventArgs, CancelableBrowserEventArgs } from '../core/utils'; import { IgxStringFilteringOperand, IgxBooleanFilteringOperand } from '../data-operations/filtering-condition'; import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface'; import { SortingDirection, ISortingExpression } from '../data-operations/sorting-expression.interface'; @@ -83,11 +83,17 @@ export enum IgxComboState { INVALID } +/** Event emitted when an igx-combo's selection is changing */ export interface IComboSelectionChangeEventArgs extends CancelableEventArgs { + /** An array containing the values that are currently selected */ oldSelection: any[]; + /** An array containing the values that will be selected after this event */ newSelection: any[]; + /** An array containing the values that will be added to the selection (if any) */ added: any[]; + /** An array containing the values that will be removed from the selection (if any) */ removed: any[]; + /** The user interaction that triggered the selection change */ event?: Event; } @@ -97,6 +103,20 @@ export interface IComboItemAdditionEvent { newCollection: any[]; } +/** + * When called with sets A & B, returns A - B (as array); + * @hidden + */ +function diffInSets(set1: Set, set2: Set): any[] { + const results = []; + set1.forEach(entry => { + if (!set2.has(entry)) { + results.push(entry); + } + }); + return results; +} + let NEXT_ID = 0; const noop = () => { }; @@ -1498,8 +1518,8 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas } protected setSelection(newSelection: Set, event?: Event): void { - const removed = Array.from(diffInSets(this.selection.get(this.id), newSelection)); - const added = Array.from(diffInSets(newSelection, this.selection.get(this.id))); + const removed = diffInSets(this.selection.get(this.id), newSelection); + const added = diffInSets(newSelection, this.selection.get(this.id)); const oldSelectionEmit = Array.from(this.selection.get(this.id) || []); const newSelectionEmit = Array.from(newSelection || []); const args: IComboSelectionChangeEventArgs = { diff --git a/projects/igniteui-angular/src/lib/core/utils.ts b/projects/igniteui-angular/src/lib/core/utils.ts index 7e335a5697c..1481ae973af 100644 --- a/projects/igniteui-angular/src/lib/core/utils.ts +++ b/projects/igniteui-angular/src/lib/core/utils.ts @@ -13,14 +13,6 @@ export function cloneArray(array: any[], deep?: boolean) { return arr; } -/** - * When called with sets A & B, returns A - B (as set); - * @hidden - */ -export function diffInSets(set1: Set, set2: Set): Set { - return new Set(Array.from(set1).filter(entry => !set2.has(entry))); -} - /** * Doesn't clone leaf items * @hidden