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

Feat - Combo - Add DisplayDensity Input to Combo and Drop Down #3007

Merged
merged 8 commits into from
Nov 19, 2018
100 changes: 50 additions & 50 deletions projects/igniteui-angular/src/lib/combo/combo.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,58 +14,58 @@
{{ item[key] }}
</ng-template>

<div class="igx-combo" [style.width]="width" role="combobox" [attr.aria-expanded]="!dropdown.collapsed" aria-haspopup="listbox" [attr.aria-owns]="dropdown.id">
<igx-input-group [type]="type" (click)="onInputClick($event)">
<input igxInput #comboInput name="comboInput" type="text" [(ngModel)]="value" readonly [placeholder]="placeholder" [disabled]="disabled" (blur)="onBlur($event)"/>
<igx-suffix *ngIf="value.length" class="clearButton" aria-label="Clear Selection" igxRipple (click)="handleClearItems($event)">
<igx-icon fontSet="material">clear</igx-icon>
</igx-suffix>
<igx-suffix igxButton="icon" class="dropdownToggleButton" igxRipple>
<igx-icon *ngIf="dropdown.collapsed; else toggleUp" fontSet="material">arrow_drop_down</igx-icon>
<ng-template #toggleUp>
<igx-icon fontSet="material">arrow_drop_up</igx-icon>
</ng-template>
</igx-suffix>
<igx-input-group [displayDensity]="displayDensity" [type]="type" (click)="onInputClick($event)">
<input igxInput #comboInput name="comboInput" type="text" [(ngModel)]="value" readonly [placeholder]="placeholder"
[disabled]="disabled" (blur)="onBlur($event)" />
<igx-suffix *ngIf="value.length" class="clearButton" aria-label="Clear Selection" igxRipple (click)="handleClearItems($event)">
<igx-icon fontSet="material">clear</igx-icon>
</igx-suffix>
<igx-suffix igxButton="icon" class="dropdownToggleButton" igxRipple>
<igx-icon *ngIf="dropdown.collapsed; else toggleUp" fontSet="material">arrow_drop_down</igx-icon>
<ng-template #toggleUp>
<igx-icon fontSet="material">arrow_drop_up</igx-icon>
</ng-template>
</igx-suffix>
</igx-input-group>
<igx-combo-drop-down #igxComboDropDown class="igx-combo__drop-down" [width]="itemsWidth || '100%'">
<igx-input-group [displayDensity]="displayDensity" class="igx-combo__search">
<input class="igx-combo-input" igxInput #searchInput name="searchInput" type="text" [(ngModel)]="searchValue"
(ngModelChange)="handleInputChange($event)" (keyup)="handleKeyUp($event)" (keydown)="handleKeyDown($event)"
(focus)="dropdown.onBlur($event)" [placeholder]="searchPlaceholder" aria-autocomplete="both"
[attr.aria-owns]="dropdown.id" [attr.aria-labelledby]="ariaLabelledBy" />
</igx-input-group>
<igx-combo-drop-down #igxComboDropDown class="igx-combo__drop-down" [width]="itemsWidth || '100%'">
<igx-input-group class="igx-combo__search">
<input class="igx-combo-input" igxInput #searchInput name="searchInput" type="text" [(ngModel)]="searchValue" (ngModelChange)="handleInputChange($event)"
(keyup)="handleKeyUp($event)" (keydown)="handleKeyDown($event)" (focus)="dropdown.onBlur($event)" [placeholder]="searchPlaceholder" aria-autocomplete="both"
[attr.aria-owns]="dropdown.id" [attr.aria-labelledby]="ariaLabelledBy" />
</igx-input-group>
<ng-container *ngTemplateOutlet="headerTemplate; context: {$implicit: this}">
</ng-container>
<div #dropdownItemContainer class="igx-combo__content" [style.overflow]="'hidden'" [style.maxHeight.px]="itemsMaxHeight"
[igxDropDownItemNavigation]="dropdown" [tabindex]="dropdown.collapsed ? -1 : 0" role="listbox" [attr.id]="dropdown.id">
<ng-template igxFor let-item [igxForOf]="data | comboFiltering:filteringExpressions:filteringLogic | comboSorting:sortingExpressions | comboGrouping:groupKey"
[igxForScrollOrientation]="'vertical'" [igxForContainerSize]="itemsMaxHeight"
[igxForItemSize]="itemHeight" (onChunkPreload)="dataLoading($event)" #virtualScrollContainer>
<igx-combo-item [value]="item" isHeader={{item.isHeader}} role="option">
<ng-container *ngIf="!item.isHeader">
<igx-checkbox [checked]="isItemSelected(item)" disableRipple="true" disabled="true" class="igx-combo__checkbox"></igx-checkbox>
</ng-container>
<ng-container *ngIf="item.isHeader">
<ng-container *ngTemplateOutlet="headerItemTemplate ? headerItemTemplate : headerItemBase; context: {$implicit: item, data: data, valueKey: valueKey, groupKey: groupKey, displayKey: displayKey}"></ng-container>
</ng-container>
<ng-container *ngIf="!item.isHeader">
<ng-container #listItem *ngTemplateOutlet="template; context: {$implicit: item, data: data, valueKey: valueKey, displayKey: displayKey};"></ng-container>
</ng-container>
</igx-combo-item>
</ng-template>
</div>
<div class="igx-combo__add" *ngIf="filteredData.length === 0 || isAddButtonVisible()">
<div class="igx-combo__empty" *ngIf="filteredData.length === 0">
<ng-container *ngTemplateOutlet="emptyTemplate ? emptyTemplate : empty; context: {$implicit: this}">
<ng-container *ngTemplateOutlet="headerTemplate; context: {$implicit: this}">
</ng-container>
<div #dropdownItemContainer class="igx-combo__content" [style.overflow]="'hidden'" [style.maxHeight.px]="itemsMaxHeight"
[igxDropDownItemNavigation]="dropdown" [tabindex]="dropdown.collapsed ? -1 : 0" role="listbox" [attr.id]="dropdown.id">
<ng-template igxFor let-item [igxForOf]="data | comboFiltering:filteringExpressions:filteringLogic | comboSorting:sortingExpressions | comboGrouping:groupKey"
[igxForScrollOrientation]="'vertical'" [igxForContainerSize]="itemsMaxHeight" [igxForItemSize]="itemHeight"
(onChunkPreload)="dataLoading($event)" #virtualScrollContainer>
<igx-combo-item [value]="item" isHeader={{item.isHeader}} role="option">
<ng-container *ngIf="!item.isHeader">
<igx-checkbox [checked]="isItemSelected(item)" disableRipple="true" disabled="true" class="igx-combo__checkbox"></igx-checkbox>
</ng-container>
<ng-container *ngIf="item.isHeader">
<ng-container *ngTemplateOutlet="headerItemTemplate ? headerItemTemplate : headerItemBase; context: {$implicit: item, data: data, valueKey: valueKey, groupKey: groupKey, displayKey: displayKey}"></ng-container>
</ng-container>
</div>
<igx-combo-item *ngIf="isAddButtonVisible()" [tabindex]="dropdown.collapsed ? -1 : customValueFlag ? 1 : -1" class="igx-combo__add-item"
igxRipple (keypress)="addItemToCollection()" [isHeader]="false" [disabled]="false" [value]="'ADD ITEM'" role="button"
aria-label="Add Item">
<ng-container *ngTemplateOutlet="addItemTemplate ? addItemTemplate : addItemDefault; context: {$implicit: this}">
<ng-container *ngIf="!item.isHeader">
<ng-container #listItem *ngTemplateOutlet="template; context: {$implicit: item, data: data, valueKey: valueKey, displayKey: displayKey};"></ng-container>
</ng-container>
</igx-combo-item>
</ng-template>
</div>
<div class="igx-combo__add" *ngIf="filteredData.length === 0 || isAddButtonVisible()">
<div class="igx-combo__empty" *ngIf="filteredData.length === 0">
<ng-container *ngTemplateOutlet="emptyTemplate ? emptyTemplate : empty; context: {$implicit: this}">
</ng-container>
</div>
<ng-container *ngTemplateOutlet="footerTemplate; context: {$implicit: this}">
</ng-container>
</igx-combo-drop-down>
</div>
<igx-combo-item *ngIf="isAddButtonVisible()" [tabindex]="dropdown.collapsed ? -1 : customValueFlag ? 1 : -1"
class="igx-combo__add-item" igxRipple (keypress)="addItemToCollection()" [isHeader]="false" [disabled]="false"
[value]="'ADD ITEM'" role="button" aria-label="Add Item">
<ng-container *ngTemplateOutlet="addItemTemplate ? addItemTemplate : addItemDefault; context: {$implicit: this}">
</ng-container>
</igx-combo-item>
</div>
<ng-container *ngTemplateOutlet="footerTemplate; context: {$implicit: this}">
</ng-container>
</igx-combo-drop-down>
28 changes: 14 additions & 14 deletions projects/igniteui-angular/src/lib/combo/combo.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1538,18 +1538,16 @@ describe('igxCombo', () => {
expect(comboWrapper.attributes.getNamedItem('ng-reflect-placeholder').nodeValue).toEqual('Items');
expect(comboWrapper.attributes.getNamedItem('ng-reflect-data').nodeValue).toEqual('Item 1,Item 2,Item 3');
expect(comboWrapper.attributes.getNamedItem('ng-reflect-filterable')).toBeTruthy();
expect(comboWrapper.childElementCount).toEqual(1);

const comboElement = comboWrapper.children[0];
expect(comboElement.attributes.getNamedItem('class').nodeValue).toEqual(CSS_CLASS_COMBO);
expect(comboElement.attributes.getNamedItem('role').nodeValue).toEqual('combobox');
expect(comboElement.style.width).toEqual(defaultComboWidth);
expect(comboElement.attributes.getNamedItem('aria-haspopup').nodeValue).toEqual('listbox');
expect(comboElement.attributes.getNamedItem('aria-expanded').nodeValue).toEqual('false');
expect(comboElement.attributes.getNamedItem('aria-owns').nodeValue).toEqual(fix.componentInstance.combo.dropdown.id);
expect(comboElement.childElementCount).toEqual(2);

const inputGroupElement = comboElement.children[0];
expect(comboWrapper.childElementCount).toEqual(2); // Input Group + Dropdown
expect(comboWrapper.attributes.getNamedItem('class').nodeValue).toEqual(CSS_CLASS_COMBO);
expect(comboWrapper.attributes.getNamedItem('role').nodeValue).toEqual('combobox');
expect(comboWrapper.style.width).toEqual(defaultComboWidth);
expect(comboWrapper.attributes.getNamedItem('aria-haspopup').nodeValue).toEqual('listbox');
expect(comboWrapper.attributes.getNamedItem('aria-expanded').nodeValue).toEqual('false');
expect(comboWrapper.attributes.getNamedItem('aria-owns').nodeValue).toEqual(fix.componentInstance.combo.dropdown.id);
expect(comboWrapper.childElementCount).toEqual(2);

const inputGroupElement = comboWrapper.children[0];
expect(inputGroupElement.attributes.getNamedItem('ng-reflect-type').nodeValue).toEqual('box');
expect(inputGroupElement.classList.contains(CSS_CLASS_INPUTGROUP)).toBeTruthy();
expect(inputGroupElement.classList.contains('igx-input-group--box')).toBeTruthy();
Expand Down Expand Up @@ -1583,7 +1581,7 @@ describe('igxCombo', () => {
expect(inputGroupBorder.classList.contains(CSS_CLASS_INPUTGROUP_BORDER)).toBeTruthy();
expect(inputGroupBorder.childElementCount).toEqual(0);

const dropDownElement = comboElement.children[1];
const dropDownElement = comboWrapper.children[1];
expect(dropDownElement.classList.contains(CSS_CLASS_COMBO_DROPDOWN)).toBeTruthy();
expect(dropDownElement.classList.contains(CSS_CLASS_DROPDOWN)).toBeTruthy();
expect(dropDownElement.attributes.getNamedItem('ng-reflect-width').nodeValue).toEqual(defaultComboDDWidth);
Expand Down Expand Up @@ -1828,6 +1826,7 @@ describe('igxCombo', () => {
expect(focusedItem_2.classList.contains(CSS_CLASS_FOCUSED)).toBeTruthy();
expect(focusedItem_1.classList.contains(CSS_CLASS_FOCUSED)).toBeFalsy();
}));

it('Should adjust combo width to the container element width when set to 100%', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxComboInContainerTestComponent);
fixture.detectChanges();
Expand All @@ -1844,7 +1843,7 @@ describe('igxCombo', () => {
combo.toggle();
tick();
fixture.detectChanges();

tick();
const inputElement = fixture.debugElement.query(By.css('.' + CSS_CLASS_INPUTGROUP_WRAPPER)).nativeElement;
const dropDownElement = fixture.debugElement.query(By.css('.' + CSS_CLASS_DROPDOWNLIST)).nativeElement;
containerElementWidth = containerElement.getBoundingClientRect().width;
Expand All @@ -1855,6 +1854,7 @@ describe('igxCombo', () => {
expect(dropDownWidth).toEqual(containerElementWidth);
expect(inputWidth).toEqual(containerElementWidth);
}));

it('Should render combo width properly when placed in container', fakeAsync(() => {
const fixture = TestBed.createComponent(IgxComboInContainerFixedWidthComponent);
fixture.detectChanges();
Expand Down
48 changes: 46 additions & 2 deletions projects/igniteui-angular/src/lib/combo/combo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { OverlaySettings, AbsoluteScrollStrategy } from '../services';
import { Subscription } from 'rxjs';
import { DeprecateProperty } from '../core/deprecateDecorators';
import { DefaultSortingStrategy, ISortingStrategy } from '../data-operations/sorting-strategy';
import { DisplayDensityBase, DisplayDensityToken, IDisplayDensityOptions } from '../core/density';

/** Custom strategy to provide the combo with callback on initial positioning */
class ComboConnectedPositionStrategy extends ConnectedPositioningStrategy {
Expand Down Expand Up @@ -97,7 +98,7 @@ const noop = () => { };
selector: 'igx-combo',
templateUrl: 'combo.component.html'
})
export class IgxComboComponent implements AfterViewInit, ControlValueAccessor, OnInit, OnDestroy {
export class IgxComboComponent extends DisplayDensityBase implements AfterViewInit, ControlValueAccessor, OnInit, OnDestroy {
/**
* @hidden
*/
Expand Down Expand Up @@ -169,7 +170,9 @@ export class IgxComboComponent implements AfterViewInit, ControlValueAccessor, O
protected elementRef: ElementRef,
protected cdr: ChangeDetectorRef,
protected selection: IgxSelectionAPIService,
@Self() @Optional() public ngControl: NgControl) {
@Self() @Optional() public ngControl: NgControl,
@Optional() @Inject(DisplayDensityToken) protected _displayDensityOptions: IDisplayDensityOptions) {
super(_displayDensityOptions);
if (this.ngControl) {
// Note: we provide the value accessor through here, instead of
// the `providers` to avoid running into a circular import.
Expand Down Expand Up @@ -523,6 +526,47 @@ export class IgxComboComponent implements AfterViewInit, ControlValueAccessor, O
return this._valid === IgxComboState.INVALID;
}

/**
* @hidden
*/
@HostBinding('class.igx-combo')
public cssClass = 'igx-combo'; // Independant of display density, at the time being

/**
* @hidden
*/
@HostBinding(`attr.role`)
public role = 'combobox';

/**
* @hidden
*/
@HostBinding('attr.aria-expanded')
public get ariaExpanded() {
return !this.dropdown.collapsed;
}

/**
* @hidden
*/
@HostBinding('attr.aria-haspopup')
public get hasPopUp() {
return 'listbox';
}

/**
* @hidden
*/
@HostBinding('attr.aria-owns')
public get ariaOwns() {
return this.dropdown.id;
}

/**
* @hidden
*/
@HostBinding('style.display')
public displayStyle = 'block';
/**
* Controls whether custom values can be added to the collection
*
Expand Down
7 changes: 7 additions & 0 deletions src/app/combo/combo.sample.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
(onAddition)="handleAddition($event)"
[data]="items"
[allowCustomValues]="true"
[displayDensity]="'comfortable'"
[filterable]="true" [displayKey]="valueKeyVar" [valueKey]="valueKeyVar"
[groupKey]="valueKeyVar ? 'region' : ''" [width]="'100%'">
<ng-template *ngIf="currentDataType !== 'primitive'" #itemTemplate let-display let-key="valueKey">
Expand Down Expand Up @@ -61,6 +62,12 @@
</div>
</ng-template>
</div>
<div>
<h4>Display Density</h4>
<button igxButton="raised" [disabled]="igxCombo.isCompact()" (click)="setDensity('compact')">Compact</button>
<button igxButton="raised" [disabled]="igxCombo.isCosy()" (click)="setDensity('cosy')">Cosy</button>
<button igxButton="raised" [disabled]="!igxCombo.isCosy() && !igxCombo.isCompact()" (click)="setDensity('comfortable')">Comfortable</button>
</div>
</section>
</div>
</div>
4 changes: 4 additions & 0 deletions src/app/combo/combo.sample.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,8 @@ export class ComboSampleComponent implements OnInit {
this.igxCombo.itemTemplate = this.initialItemTemplate ? this.initialItemTemplate : this.customItemTemplate ;
this.initialItemTemplate = comboTemplate;
}

setDensity(density: string) {
this.igxCombo.displayDensity = density;
}
}