Skip to content

Commit 287b048

Browse files
authored
Merge branch 'master' into astaev/tabsStyleMigrations
2 parents fc7e4b3 + 54f1b58 commit 287b048

File tree

5 files changed

+152
-18
lines changed

5 files changed

+152
-18
lines changed

CHANGELOG.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,23 @@ All notable changes for each version of this project will be documented in this
6464
- The `IgxBottomNav` component exposes `disableAnimations` property which determines whether the contents should animate when switching the selected item. The property is set to `true` by default which means that the animations are disabled.
6565
- `IgxOverlayService`
6666
- `detach` and `detachAll` methods are added to `IgxOverlayService`. Calling `detach` will remove all the elements generated for the related overlay, as well as clean up all related resources. Calling `detachAll` will remove all elements generated by any call to `IgxOverlay` `attach`, and will clean up all related resources. _Note: calling `hide` or `hideAll` will not clean generated by the service elements and will not clean up related resources._
67-
67+
- `IgxCombo`
68+
- Any changes to `IComboItemAdditionEvent.addedItem` will be reflected in the item added to the data.
69+
- `IgxComboComponent.onAddition` is now cancelable. You can prevent the item from being added by setting the event args `cancel` property to `true`.
70+
```typescript
71+
public handleComboItemAddition(event: IComboItemAdditionEvent): void {
72+
if (event.addedItem[this.combo.valueKey] === 'ForbiddenValue') {
73+
event.cancel = true;
74+
} else if (event.addedItem[this.combo.valueKey] === 'Change Me') {
75+
const index = this.iter++;
76+
event.addedItem = {
77+
[this.combo.valueKey]: `customId${index}`,
78+
[this.combo.displayKey]: `New item ${index}`;
79+
}
80+
}
81+
}
82+
```
83+
6884
### Themes:
6985
- Breaking Changes:
7086
- `IgxButton` theme has been simplified. The number of theme params (`igx-button-theme`) has been reduced significantly and no longer includes prefixed parameters (`flat-*`, `raised-*`, etc.). See the migration guide to update existing button themes. Updates performed with `ng update` will migrate existing button themes but some additional tweaking may be required to account for the abscense of prefixed params.

projects/igniteui-angular/src/lib/combo/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ Setting `[displayDensity]` affects the control's items' and inputs' css properti
334334
|------------------ |-------------------------------------------------------------------------|------------- |-----------------------------------------|
335335
| `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) |
336336
| `onSearchInput` | Emitted when an the search input's input event is triggered | true | { searchValue: `string` } |
337-
| `onAddition` | Emitted when an item is being added to the data collection | false | { oldCollection: `any[]`, addedItem: `<any>`, newCollection: `any[]` }|
337+
| `onAddition` | Emitted when an item is being added to the data collection | true | { oldCollection: `any[]`, addedItem: `<any>`, newCollection: `any[]`, owner?: `IgxComboComponent`, cancel: `boolean` }|
338338
| `onDataPreLoad` | Emitted when new chunk of data is loaded from the virtualization | false | { event: `Event` } |
339339
| `onOpening` | Emitted before the dropdown is opened | false | `IBaseCancelableBrowserEventArgs` |
340340
| `onOpened` | Emitted after the dropdown is opened | false | { event: `Event` } |

projects/igniteui-angular/src/lib/combo/combo.component.spec.ts

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
IgxComboModule,
99
IComboSelectionChangeEventArgs,
1010
IgxComboState,
11-
IComboSearchInputEventArgs
11+
IComboSearchInputEventArgs,
12+
IComboItemAdditionEvent
1213
} from './combo.component';
1314
import { IgxComboItemComponent } from './combo-item.component';
1415
import { IgxComboDropDownComponent } from './combo-dropdown.component';
@@ -685,6 +686,106 @@ describe('igxCombo', () => {
685686
combo.handleClearItems(spyObj);
686687
expect(combo.value).toEqual(item[0]);
687688
});
689+
690+
it('should allow canceling and overwriting of item addition', fakeAsync(() => {
691+
const selectionService = new IgxSelectionAPIService();
692+
combo = new IgxComboComponent(elementRef, mockCdr, selectionService, mockComboService,
693+
mockIconService, null, null, mockInjector);
694+
const dropdown = jasmine.createSpyObj('IgxComboDropDownComponent', ['selectItem']);
695+
const mockVirtDir = jasmine.createSpyObj('virtDir', ['scrollTo']);
696+
const mockInput = jasmine.createSpyObj('mockInput', [], {
697+
nativeElement: jasmine.createSpyObj('mockElement', ['focus'])
698+
});
699+
spyOn(combo.onAddition, 'emit').and.callThrough();
700+
spyOn(mockIconService, 'addSvgIconFromText').and.returnValue(null);
701+
const subParams: { cancel: boolean; newValue: string; modify: boolean } = {
702+
cancel: false,
703+
modify: false,
704+
newValue: 'mockValue'
705+
};
706+
const sub = combo.onAddition.subscribe((e) => {
707+
if (subParams.cancel) {
708+
e.cancel = true;
709+
}
710+
if (subParams.modify) {
711+
e.addedItem = subParams.newValue;
712+
}
713+
});
714+
715+
combo.ngOnInit();
716+
combo.data = ['Item 1', 'Item 2', 'Item 3'];
717+
combo.dropdown = dropdown;
718+
combo.searchInput = mockInput;
719+
(combo as any).virtDir = mockVirtDir;
720+
let mockAddParams: IComboItemAdditionEvent = {
721+
cancel: false,
722+
owner: combo,
723+
addedItem: 'Item 99',
724+
newCollection: ['Item 1', 'Item 2', 'Item 3', 'Item 99'],
725+
oldCollection: ['Item 1', 'Item 2', 'Item 3']
726+
};
727+
728+
729+
// handle addition
730+
731+
combo.searchValue = 'Item 99';
732+
combo.addItemToCollection();
733+
tick();
734+
expect(combo.data.length).toEqual(4);
735+
expect(combo.onAddition.emit).toHaveBeenCalledWith(mockAddParams);
736+
expect(combo.onAddition.emit).toHaveBeenCalledTimes(1);
737+
expect(mockVirtDir.scrollTo).toHaveBeenCalledTimes(1);
738+
expect(combo.searchInput.nativeElement.focus).toHaveBeenCalledTimes(1);
739+
expect(combo.data[combo.data.length - 1]).toBe('Item 99');
740+
expect(selectionService.get(combo.id).size).toBe(1);
741+
expect([...selectionService.get(combo.id)][0]).toBe('Item 99');
742+
743+
// cancel
744+
subParams.cancel = true;
745+
mockAddParams = {
746+
cancel: true,
747+
owner: combo,
748+
addedItem: 'Item 99',
749+
newCollection: ['Item 1', 'Item 2', 'Item 3', 'Item 99', 'Item 99'],
750+
oldCollection: ['Item 1', 'Item 2', 'Item 3', 'Item 99']
751+
};
752+
753+
combo.searchValue = 'Item 99';
754+
combo.addItemToCollection();
755+
tick();
756+
expect(combo.onAddition.emit).toHaveBeenCalledWith(mockAddParams);
757+
expect(combo.onAddition.emit).toHaveBeenCalledTimes(2);
758+
expect(mockVirtDir.scrollTo).toHaveBeenCalledTimes(1);
759+
expect(combo.searchInput.nativeElement.focus).toHaveBeenCalledTimes(1);
760+
expect(combo.data.length).toEqual(4);
761+
expect(combo.data[combo.data.length - 1]).toBe('Item 99');
762+
expect(selectionService.get(combo.id).size).toBe(1);
763+
expect([...selectionService.get(combo.id)][0]).toBe('Item 99');
764+
765+
// overwrite
766+
subParams.modify = true;
767+
subParams.cancel = false;
768+
mockAddParams = {
769+
cancel: false,
770+
owner: combo,
771+
addedItem: 'mockValue',
772+
newCollection: ['Item 1', 'Item 2', 'Item 3', 'Item 99', 'Item 99'],
773+
oldCollection: ['Item 1', 'Item 2', 'Item 3', 'Item 99']
774+
};
775+
776+
combo.searchValue = 'Item 99';
777+
combo.addItemToCollection();
778+
tick();
779+
expect(combo.onAddition.emit).toHaveBeenCalledWith(mockAddParams);
780+
expect(combo.onAddition.emit).toHaveBeenCalledTimes(3);
781+
expect(mockVirtDir.scrollTo).toHaveBeenCalledTimes(2);
782+
expect(combo.searchInput.nativeElement.focus).toHaveBeenCalledTimes(2);
783+
expect(combo.data.length).toEqual(5);
784+
expect(combo.data[combo.data.length - 1]).toBe(subParams.newValue);
785+
expect(selectionService.get(combo.id).size).toBe(2);
786+
expect([...selectionService.get(combo.id)][1]).toBe(subParams.newValue);
787+
sub.unsubscribe();
788+
}));
688789
});
689790
describe('Initialization and rendering tests: ', () => {
690791
configureTestSuite();

projects/igniteui-angular/src/lib/combo/combo.component.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import { FormsModule, ReactiveFormsModule, ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl, AbstractControl } from '@angular/forms';
1717
import { IgxCheckboxModule } from '../checkbox/checkbox.component';
1818
import { IgxSelectionAPIService } from '../core/selection';
19-
import { cloneArray, IBaseEventArgs, IBaseCancelableBrowserEventArgs, IBaseCancelableEventArgs } from '../core/utils';
19+
import { cloneArray, IBaseEventArgs, IBaseCancelableBrowserEventArgs, IBaseCancelableEventArgs, CancelableEventArgs } from '../core/utils';
2020
import { IgxStringFilteringOperand, IgxBooleanFilteringOperand } from '../data-operations/filtering-condition';
2121
import { FilteringLogic } from '../data-operations/filtering-expression.interface';
2222
import { IgxForOfModule, IForOfState, IgxForOfDirective } from '../directives/for-of/for_of.directive';
@@ -110,7 +110,7 @@ export interface IComboSearchInputEventArgs extends IBaseCancelableEventArgs {
110110
searchText: string;
111111
}
112112

113-
export interface IComboItemAdditionEvent extends IBaseEventArgs {
113+
export interface IComboItemAdditionEvent extends IBaseEventArgs, CancelableEventArgs {
114114
oldCollection: any[];
115115
addedItem: any;
116116
newCollection: any[];
@@ -783,8 +783,8 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
783783
*/
784784
@Input()
785785
public get type(): IgxInputGroupType {
786-
return this._type || this._inputGroupType || 'box';
787-
}
786+
return this._type || this._inputGroupType || 'box';
787+
}
788788

789789
public set type(val: IgxInputGroupType) {
790790
this._type = val;
@@ -1107,18 +1107,21 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
11071107
if (this.groupKey) {
11081108
Object.assign(addedItem, { [this.groupKey]: this.defaultFallbackGroup });
11091109
}
1110-
const oldCollection = this.data;
1111-
const newCollection = [...this.data];
1112-
newCollection.push(addedItem);
1110+
// expose shallow copy instead of this.data in event args so this.data can't be mutated
1111+
const oldCollection = [...this.data];
1112+
const newCollection = [...this.data, addedItem];
11131113
const args: IComboItemAdditionEvent = {
1114-
oldCollection, addedItem, newCollection, owner: this
1114+
oldCollection, addedItem, newCollection, owner: this, cancel: false
11151115
};
11161116
this.onAddition.emit(args);
1117-
this.data.push(addedItem);
1118-
// If you mutate the array, no pipe is invoked and the display isn't updated;
1119-
// if you replace the array, the pipe executes and the display is updated.
1117+
if (args.cancel) {
1118+
return;
1119+
}
1120+
this.data.push(args.addedItem);
1121+
// trigger re-render
11201122
this.data = cloneArray(this.data);
1121-
this.selectItems(this.comboAPI.valueKey !== null ? [addedItem[this.valueKey]] : [addedItem], false);
1123+
this.selectItems(this.valueKey !== null && this.valueKey !== undefined ?
1124+
[args.addedItem[this.valueKey]] : [args.addedItem], false);
11221125
this.customValueFlag = false;
11231126
this.searchInput.nativeElement.focus();
11241127
this.dropdown.focusedItem = null;
@@ -1553,9 +1556,9 @@ export class IgxComboComponent extends DisplayDensityBase implements IgxComboBas
15531556
*/
15541557
private getValueDisplayPairs(ids: any[]) {
15551558
return this.data.filter(entry => ids.indexOf(entry[this.valueKey]) > -1).map(e => ({
1556-
[this.valueKey]: e[this.valueKey],
1557-
[this.displayKey]: e[this.displayKey]
1558-
}));
1559+
[this.valueKey]: e[this.valueKey],
1560+
[this.displayKey]: e[this.displayKey]
1561+
}));
15591562
}
15601563

15611564
private checkMatch(): void {

projects/igniteui-angular/src/lib/grids/grid/grid.component.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,12 +775,26 @@ export class IgxGridComponent extends IgxGridBaseDirective implements GridType,
775775
return this.columnList.some((col) => col.groupable && !col.columnGroup);
776776
}
777777

778+
/**
779+
* Returns whether the `IgxGridComponent` has group area.
780+
*
781+
* @example
782+
* ```typescript
783+
* let isGroupAreaVisible = this.grid.showGroupArea;
784+
* ```
785+
*
786+
* @example
787+
* ```html
788+
* <igx-grid #grid [data]="Data" [showGroupArea]="false"></igx-grid>
789+
* ```
790+
*/
778791
@Input()
779792
public get showGroupArea(): boolean {
780793
return this._showGroupArea;
781794
}
782795
public set showGroupArea(value: boolean) {
783796
this._showGroupArea = value;
797+
this.notifyChanges(true);
784798
}
785799

786800
/**

0 commit comments

Comments
 (0)