Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -129,33 +129,43 @@
[class.is-loading]="item.loading !== VirtualScrollItemStatus.loaded"
[class.readonly]="item.loading !== VirtualScrollItemStatus.loaded"
[class.selected]="!multiple &&
isItemSelected(item)"
isItemSelected(item)"
[style.height.px]="itemSize"
(click)="preventDefault($event);
updateValue(item, !multiple, true);"
updateValue(item, !multiple, true);"
matRipple>
<div [matTooltip]="disableTooltip ? null : item.text"
matTooltipPosition="right"
class="ui-suggest-item">
<mat-checkbox *ngIf="multiple"
[checked]="isItemSelected(item)"
[indeterminate]="false">
</mat-checkbox>
<div [attr.aria-label]="item.text"
class="text-label text-ellipsis"
tabindex="-1">
<mat-icon *ngIf="!!item?.icon?.svgIcon"
[svgIcon]="item?.icon.svgIcon"></mat-icon>
<mat-icon *ngIf="!!item?.icon?.matIcon">{{item?.icon.matIcon}}</mat-icon>
{{ item?.icon?.iconOnly ? null : item.text }}
<ng-container *ngIf="itemTemplate; else defaultItem">
<ng-container *ngTemplateOutlet="itemTemplate; context: {
$implicit: item
}">
</ng-container>
</ng-container>
<ng-template #defaultItem>
<div [matTooltip]="disableTooltip ? null : item.text"
matTooltipPosition="right"
class="ui-suggest-item">
<mat-checkbox *ngIf="multiple"
[checked]="isItemSelected(item)"
[indeterminate]="false">
</mat-checkbox>
<div [attr.aria-label]="item.text"
class="text-label text-ellipsis"
tabindex="-1">
<mat-icon *ngIf="!!item?.icon?.svgIcon"
[svgIcon]="item?.icon.svgIcon"></mat-icon>
<mat-icon *ngIf="!!item?.icon?.matIcon">{{item?.icon.matIcon}}</mat-icon>
{{ item?.icon?.iconOnly ? null : item.text }}
</div>
</div>
</div>
</ng-template>
</mat-list-item>
</ng-container>

<ng-container *ngTemplateOutlet="customValueTemplate; context: { $implicit: 'up' }">
</ng-container>

<mat-list-item *ngIf="loading$ | async">
<mat-list-item *ngIf="loading$ | async"
[style.height.px]="itemSize">
<mat-progress-spinner color="primary"
mode="indeterminate"
diameter="20"
Expand All @@ -180,6 +190,7 @@
<mat-list-item *ngIf="isCustomValueVisible && direction === renderDirection"
[class.active]="(renderDirection === 'down' ? -1 : items.length) === activeIndex"
[matTooltip]="customValueLabelTranslator(inputControl.value)"
[style.height.px]="itemSize"
(click)="preventDefault($event); updateValue(inputControl.value, !multiple, true);"
matTooltipPosition="right"
class="text-ellipsis custom-item">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class UiSuggestFixture {

public clearable?: boolean;
public searchable?: boolean;
public alwaysExpanded?: boolean;
public disabled?: boolean;
public multiple?: boolean;
public readonly?: boolean;
Expand Down Expand Up @@ -133,7 +134,7 @@ const sharedSpecifications = (
expect(displayValue.nativeElement.innerText.trim()).toEqual(component.defaultValue);
});

it('should remove NULL or Undefiend entries', () => {
it('should remove NULL or Undefined entries', () => {
const item = generateSuggestionItem();
component.value = [undefined, null, null, undefined, item, null, undefined] as ISuggestValue[];

Expand Down Expand Up @@ -238,6 +239,32 @@ const sharedSpecifications = (
expect(itemListEntries.length).toEqual(0);
});

it('should render the list open and not close on selection if alwaysExpanded is true', (async () => {
const items = generateSuggetionItemList(10);

component.alwaysExpanded = true;
component.items = items;

fixture.detectChanges();
await fixture.whenStable();

const itemListEntries = fixture.debugElement.queryAll(By.css('.mat-list-item'));

expect(itemListEntries).not.toBeNull();
expect(itemListEntries.length).toEqual(items.length);

const itemIndex = Math.floor(Math.random() * items.length);
const currentListItem = fixture.debugElement.queryAll(
By.css('.mat-list-item'),
)[itemIndex];

currentListItem.nativeElement.dispatchEvent(EventGenerator.click);
fixture.detectChanges();

expect(itemListEntries).not.toBeNull();
expect(itemListEntries.length).toEqual(items.length);
}));

it('should filter items if typed into', (done) => {
let items = generateSuggetionItemList(40);
const filteredItem: ISuggestValue = {
Expand Down Expand Up @@ -1037,9 +1064,9 @@ const sharedSpecifications = (
const word = faker.random.word();
const wordWithWhitespace = `${
Array(6).fill(' ').join('')
}${word}${
}${word}${
Array(6).fill(' ').join('')
}`;
}`;

searchFor(wordWithWhitespace, fixture);
await fixture.whenStable();
Expand Down Expand Up @@ -1597,6 +1624,7 @@ describe('Component: UiSuggest', () => {
[clearable]="clearable"
[searchable]="searchable"
[enableCustomValue]="enableCustomValue"
[alwaysExpanded]="alwaysExpanded"
[items]="items"
[value]="value"
[direction]="direction"
Expand Down Expand Up @@ -1673,6 +1701,7 @@ describe('Component: UiSuggest', () => {
[clearable]="clearable"
[searchable]="searchable"
[enableCustomValue]="enableCustomValue"
[alwaysExpanded]="alwaysExpanded"
[items]="items"
[value]="value"
[direction]="direction"
Expand Down Expand Up @@ -1837,4 +1866,83 @@ describe('Component: UiSuggest', () => {
});
});
});


@Component({
template: `
<ui-suggest [placeholder]="placeholder"
[defaultValue]="defaultValue"
[clearable]="clearable"
[searchable]="searchable"
[enableCustomValue]="enableCustomValue"
[alwaysExpanded]="alwaysExpanded"
[items]="items"
[value]="value"
[direction]="direction"
[displayPriority]="displayPriority"
[disabled]="disabled"
[multiple]="multiple"
[readonly]="readonly">
<ng-template let-item >
<div class="item-template">{{ item.text }}</div>
</ng-template>
</ui-suggest>
`,
})
class UiSuggestCustomTemplateFixtureComponent extends UiSuggestFixture { }

describe('Type: custom template', () => {
let fixture: ComponentFixture<UiSuggestCustomTemplateFixtureComponent>;
let component: UiSuggestCustomTemplateFixtureComponent;

const beforeEachFn = () => {
TestBed.configureTestingModule({
imports: [
UiSuggestModule,
ReactiveFormsModule,
MatInputModule,
NoopAnimationsModule,
],
declarations: [
UiSuggestCustomTemplateFixtureComponent,
],
});

const compFixture = TestBed.createComponent(UiSuggestCustomTemplateFixtureComponent);

return {
fixture: compFixture,
component: compFixture.componentInstance,
uiSuggest: compFixture.componentInstance.uiSuggest,
};
};

describe('Behavior: Specific', () => {
beforeEach(() => {
const setup = beforeEachFn();
fixture = setup.fixture;
component = setup.component;
});

it('should render the list items using the provided custom template', (async () => {
const items = generateSuggetionItemList(5);
component.items = items;

fixture.detectChanges();
const display = fixture.debugElement.query(By.css('.display'));
display.nativeElement.dispatchEvent(EventGenerator.click);

fixture.detectChanges();
await fixture.whenStable();

const generatedItems = fixture.debugElement.queryAll(By.css('ui-suggest .item-template'));

expect(items.length).toBe(generatedItems.length);

items.forEach((item, index) => {
expect(item.text).toBe(generatedItems[index].nativeElement.innerText);
});
}));
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild,
ElementRef,
EventEmitter,
HostBinding,
Expand All @@ -16,6 +17,7 @@ import {
Optional,
Output,
Self,
TemplateRef,
ViewChild,
ViewEncapsulation,
} from '@angular/core';
Expand Down Expand Up @@ -144,6 +146,15 @@ export class UiSuggestComponent extends UiSuggestMatFormField
this._cd.detectChanges();
}


/**
* If true, the item list will render open and will not close on selection
*
* @ignore
*/
@Input()
public alwaysExpanded = false;

/**
* Configure if the component allows multi-selection.
*
Expand Down Expand Up @@ -223,6 +234,13 @@ export class UiSuggestComponent extends UiSuggestMatFormField
this.searchSourceFactory = (searchTerm = '') => inMemorySearch(searchTerm, this._lastSetItems);
}

/**
* Reference for custom item template
*
*/
@ContentChild(TemplateRef, { static: true })
public itemTemplate: TemplateRef<any> | null = null;

/**
* Computes the current tooltip value.
*
Expand Down Expand Up @@ -508,6 +526,10 @@ export class UiSuggestComponent extends UiSuggestMatFormField
* @ignore
*/
ngOnInit() {
if (this.alwaysExpanded) {
this.open();
}

merge(
this._reset$.pipe(
map(_ => ''),
Expand Down Expand Up @@ -656,7 +678,7 @@ export class UiSuggestComponent extends UiSuggestMatFormField
* @param [refocus=true] If the dropdown should be focused after closing.
*/
public close(refocus = true) {
if (!this.isOpen) { return; }
if (this.alwaysExpanded || !this.isOpen) { return; }

if (this._isOnCustomValueIndex && !this.loading$.value) {
if (!this.multiple) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,20 @@ export abstract class UiSuggestMatFormField implements
this.stateChanges.next();
}

/**
* Set a custom size for the list items.
*
*/
@Input()
public customItemSize?: number;

/**
* Computes the component item height depending on the current render mode.
*
*/
public get itemSize() {
if (this.customItemSize) { return this.customItemSize; }

return this.isFormControl ? 32 : 40;
}

Expand Down