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(combo): Handle selection when placed in an IgxGrid cell template and grid is virtualized - master #14414

Merged
16 changes: 15 additions & 1 deletion projects/igniteui-angular/src/lib/combo/combo.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,20 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh
*/
@HostBinding('attr.id')
@Input()
public id = `igx-combo-${NEXT_ID++}`;
public get id(): string {
return this._id;
}

public set id(value: string) {
if (!value) {
return;
}
const selection = this.selectionService.get(this._id);
this._id = value;
if (selection) {
this.selectionService.set(this._id, selection);
}
}

/**
* Sets the style width of the element
Expand Down Expand Up @@ -943,6 +956,7 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh
protected compareCollator = new Intl.Collator();
protected computedStyles;

private _id: string = `igx-combo-${NEXT_ID++}`;
private _type = null;
private _dataType = '';
private _itemHeight = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { AbsoluteScrollStrategy, AutoPositionStrategy, ConnectedPositioningStrat
import { configureTestSuite } from '../test-utils/configure-suite';
import { UIInteractions, wait } from '../test-utils/ui-interactions.spec';
import { IgxSimpleComboComponent, ISimpleComboSelectionChangingEventArgs } from './public_api';
import { IgxGridComponent } from '../grids/grid/grid.component';
import { IGX_GRID_DIRECTIVES } from '../grids/grid/public_api';


const CSS_CLASS_COMBO = 'igx-combo';
Expand Down Expand Up @@ -2447,8 +2449,109 @@ describe('IgxSimpleCombo', () => {
expect(input.nativeElement.value).toEqual('Product 5');
}));
});

describe('Integration', () => {
let grid: IgxGridComponent;

beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxSimpleComboInGridComponent
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(IgxSimpleComboInGridComponent);
fixture.detectChanges();
grid = fixture.componentInstance.grid;
});
it('Combo in IgxGrid cell display template correctly handles selection - issue #14305', async () => {
const firstRecRegionCell = grid.gridAPI.get_cell_by_index(0, 'Region') as any;
let comboNativeEl = firstRecRegionCell.nativeElement.querySelector(SIMPLE_COMBO_ELEMENT);
const comboToggleButton = comboNativeEl.querySelector(`.${CSS_CLASS_TOGGLEBUTTON}`);

UIInteractions.simulateClickEvent(comboToggleButton);
fixture.detectChanges();

const comboDropDownList = fixture.debugElement.query(By.css(`.${CSS_CLASS_DROPDOWNLIST}`));
const firstItem = comboDropDownList.nativeElement.querySelector(`.${CSS_CLASS_DROPDOWNLISTITEM}`);

UIInteractions.simulateClickEvent(firstItem);
fixture.detectChanges();

const firstRegionCellObject = grid.getCellByColumn(0, 'Region');
expect(firstRegionCellObject.value).toEqual(fixture.componentInstance.regions[0]);

try {
// combo should not throw from the selection getter at this point
grid.navigateTo(fixture.componentInstance.data.length - 1, 0);
await wait(30);
fixture.detectChanges();
} catch (error) {
fail(`Test failed with error: ${error}`)
}

const virtState = grid.verticalScrollContainer.state;
expect(virtState.startIndex).toBe(grid.dataView.length - virtState.chunkSize);

// These will fail in case the editor (combo) in the cell display template is not bound to the cell value
// as the first record's selected value will be applied on the reused combos bc of the virtualization
for (let i = virtState.startIndex; i < virtState.startIndex + virtState.chunkSize && i < grid.dataView.length; i++) {
const targetCell = grid.gridAPI.get_cell_by_index(i, 'Region') as any;
comboNativeEl = targetCell.nativeElement.querySelector(SIMPLE_COMBO_ELEMENT);
const comboInput = comboNativeEl.querySelector('input');
expect(comboInput.value).toBe('', `Failed on index: ${i.toString()}`);
}

for (let i = virtState.startIndex; i < virtState.startIndex + virtState.chunkSize && i < grid.dataView.length; i++) {
const cell = grid.getCellByColumn(i, 'Region');
expect(cell.value).toBe(undefined);
}
});
});
});

@Component({
template: `
<igx-grid #grid [data]="data" [autoGenerate]="false" width="100%" height="500px" [primaryKey]="'ID'">
<igx-column field="ID" header="ID" [dataType]="'number'" width="100px">
</igx-column>
<igx-column field="Region" header="Region" dataType="string" width="220px">
<ng-template igxCell let-cell="cell">
<div>
<igx-simple-combo [id]="'region-' + cell.row.data.ID"
[data]="regions" placeholder="Choose Region..."
[(ngModel)]="cell.value"
>
</igx-simple-combo>
</div>
</ng-template>
</igx-column>
</igx-grid>
`,
standalone: true,
imports: [IgxSimpleComboComponent, IGX_GRID_DIRECTIVES, FormsModule]
})
class IgxSimpleComboInGridComponent {
@ViewChild('grid', { read: IgxGridComponent, static: true })
public grid: IgxGridComponent;

public data = [];
public regions = [];
constructor() {
for (let i = 1; i <= 15; i++) {
this.data.push({
ID: i,
region: undefined
});
}
for (let i = 1; i <= 5; i++) {
this.regions.push(`Region ${i}`);
}
}
}

@Component({
template: `
<igx-simple-combo #combo [placeholder]="'Location'" [data]='items' [style.--ig-size]="'var(--ig-size-' + size + ')'"
Expand Down
Loading