Skip to content

Commit bb862bf

Browse files
authored
Merge pull request #240 from UiPath/feat/ui-suggest-compact-summary
feat(suggest): add compact summary support
2 parents f89669c + 22f98e5 commit bb862bf

File tree

5 files changed

+159
-34
lines changed

5 files changed

+159
-34
lines changed

projects/angular/components/ui-suggest/src/ui-suggest.component.html

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
Tested in NVDA.
1010
-->
1111

12-
<ng-container *ngIf="!multiple; else chipsTemplate">
12+
<ng-container *ngIf="(!multiple || compact); else chipsTemplate">
1313
<div [attr.tabindex]="!disabled &&
1414
!readonly ?
1515
0 : null"
@@ -35,8 +35,7 @@
3535
matRipple
3636
class="display"
3737
role="combobox">
38-
<ng-container
39-
*ngIf="itemTemplate && !multiple && value.length && displayTemplateValue; else showDefaultValue">
38+
<ng-container *ngIf="itemTemplate && value.length && displayTemplateValue; else showDefaultValue">
4039
<ng-container *ngTemplateOutlet="itemTemplate; context: {
4140
$implicit: value[0]
4241
}">
@@ -50,10 +49,16 @@
5049
aria-hidden="true">
5150
{{ placeholder }}:
5251
</span>
53-
<span class="display-value text-label-rendered">{{
54-
value[0]
55-
? intl.translateLabel(value[0].text)
56-
: defaultValue }}</span>
52+
<ng-container *ngIf="compactSummaryTemplate; else defaultSummary">
53+
<ng-template #displayCompactSummaryTemplate
54+
*ngTemplateOutlet="compactSummaryTemplate; context: { $implicit: value }">
55+
</ng-template>
56+
</ng-container>
57+
<ng-template #defaultSummary>
58+
<span class="display-value text-label-rendered">
59+
{{ computeDisplayValue() }}
60+
</span>
61+
</ng-template>
5762
</div>
5863
</ng-template>
5964
<ng-container *ngIf="!readonly">
@@ -153,7 +158,7 @@
153158
</ng-container>
154159
</ng-container>
155160

156-
<mat-form-field *ngIf="searchable && !multiple"
161+
<mat-form-field *ngIf="searchable && (!multiple || compact)"
157162
floatLabel="never">
158163
<input #searchInput
159164
[placeholder]="isFormControl ? '' : placeholder"
@@ -310,7 +315,6 @@
310315
</mat-list-item>
311316
</ng-template>
312317

313-
314318
<ng-template #clearIcon>
315319
<mat-icon [matTooltip]="intl.clearSelectionLabel"
316320
(keydown.space)="removeSelection($event)"

projects/angular/components/ui-suggest/src/ui-suggest.component.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ $componentName: "ui-suggest";
187187
&-container {
188188
max-width: calc(100% - #{$mat-icon-size});
189189
}
190+
191+
.mat-icon {
192+
flex-shrink: 0;
193+
}
190194
}
191195

192196
.mat-chip {

projects/angular/components/ui-suggest/src/ui-suggest.component.spec.ts

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class UiSuggestFixtureDirective {
7474

7575
clearable?: boolean;
7676
searchable?: boolean;
77+
compact?: boolean;
7778
searchableCountInfo?: { count: number; message: string };
7879
alwaysExpanded?: boolean;
7980
disabled?: boolean;
@@ -1655,6 +1656,30 @@ const sharedSpecifications = (
16551656
.forEach(valueText => expect(selectedChipsInnerText.includes(valueText)).toBeTrue());
16561657
});
16571658

1659+
it('should display all selected values in compact mode', () => {
1660+
const selectedValues = faker.helpers.shuffle(component.items!).slice(0, 3);
1661+
component.value = selectedValues;
1662+
component.compact = true;
1663+
component.searchable = true;
1664+
1665+
fixture.detectChanges();
1666+
1667+
const displayContainer = fixture.debugElement.query(By.css('.display-container'));
1668+
const displayValue = displayContainer.query(By.css('.display-value'));
1669+
1670+
if (!uiSuggest.isFormControl) {
1671+
const displayTitle = displayContainer.query(By.css('.display-title'));
1672+
expect(displayTitle.nativeElement.innerText.trim()).toBe(`${component.placeholder}:`);
1673+
} else {
1674+
const displayTitle = fixture.debugElement.query(By.css('.mat-form-field-label'));
1675+
expect(displayTitle.nativeElement.innerText.trim()).toEqual(component.placeholder);
1676+
}
1677+
1678+
selectedValues
1679+
.map(value => value.text)
1680+
.forEach(valueText => expect(displayValue.nativeElement.innerText.trim()).toContain(valueText));
1681+
});
1682+
16581683
it('should have a checkbox next to each item entry', waitForAsync(async () => {
16591684
fixture.detectChanges();
16601685

@@ -2428,7 +2453,8 @@ describe('Component: UiSuggest', () => {
24282453
[fetchStrategy]="fetchStrategy"
24292454
[minChars]="minChars"
24302455
[drillDown]="drillDown"
2431-
[readonly]="readonly">
2456+
[readonly]="readonly"
2457+
[compact]="compact">
24322458
</ui-suggest>
24332459
`,
24342460
})
@@ -2509,6 +2535,7 @@ describe('Component: UiSuggest', () => {
25092535
[fetchStrategy]="fetchStrategy"
25102536
[minChars]="minChars"
25112537
[drillDown]="drillDown"
2538+
[compact]="compact"
25122539
formControlName="test">
25132540
</ui-suggest>
25142541
</mat-form-field>
@@ -2682,32 +2709,45 @@ describe('Component: UiSuggest', () => {
26822709

26832710
@Component({
26842711
template: `
2685-
<ui-suggest [placeholder]="placeholder"
2686-
[defaultValue]="defaultValue"
2687-
[clearable]="clearable"
2688-
[searchable]="searchable"
2689-
[enableCustomValue]="enableCustomValue"
2690-
[alwaysExpanded]="alwaysExpanded"
2691-
[headerItems]="headerItems"
2692-
[items]="items"
2693-
[value]="value"
2694-
[direction]="direction"
2695-
[displayPriority]="displayPriority"
2696-
[disabled]="disabled"
2697-
[multiple]="multiple"
2698-
[readonly]="readonly"
2699-
[fetchStrategy]="fetchStrategy"
2700-
[displayTemplateValue]="displayTemplateValue"
2701-
[searchableCountInfo]="searchableCountInfo"
2702-
[minChars]="minChars"
2703-
[drillDown]="drillDown">
2704-
<ng-template let-item >
2705-
<div class="item-template">{{ item.text }}</div>
2706-
</ng-template>
2712+
<ui-suggest
2713+
[placeholder]="placeholder"
2714+
[defaultValue]="defaultValue"
2715+
[clearable]="clearable"
2716+
[searchable]="searchable"
2717+
[enableCustomValue]="enableCustomValue"
2718+
[alwaysExpanded]="alwaysExpanded"
2719+
[headerItems]="headerItems"
2720+
[items]="items"
2721+
[value]="value"
2722+
[direction]="direction"
2723+
[displayPriority]="displayPriority"
2724+
[disabled]="disabled"
2725+
[multiple]="multiple"
2726+
[readonly]="readonly"
2727+
[fetchStrategy]="fetchStrategy"
2728+
[displayTemplateValue]="displayTemplateValue"
2729+
[searchableCountInfo]="searchableCountInfo"
2730+
[minChars]="minChars"
2731+
[drillDown]="drillDown"
2732+
[compact]="compact"
2733+
[compactSummaryTemplate]="compactSummaryTemplate"
2734+
>
2735+
<ng-template let-item>
2736+
<div class="item-template">{{ item.text }}</div>
2737+
</ng-template>
27072738
</ui-suggest>
2739+
<ng-template #compactSummaryTemplate let-value>
2740+
<div class="custom-display-value">
2741+
<span class="text-ellipsis">{{value?.[0]?.text}}</span>
2742+
<span *ngIf="(value?.length || 0) > 1">
2743+
(+{{ (value?.length || 0) - 1 }}
2744+
{{ value?.length === 2 ? "other" : "others" }})
2745+
</span>
2746+
</div>
2747+
</ng-template>
27082748
`,
27092749
})
2710-
class UiSuggestCustomTemplateFixtureComponent extends UiSuggestFixtureDirective { }
2750+
class UiSuggestCustomTemplateFixtureComponent extends UiSuggestFixtureDirective {}
27112751

27122752
describe('Type: custom template', () => {
27132753
let fixture: ComponentFixture<UiSuggestCustomTemplateFixtureComponent>;
@@ -2762,6 +2802,25 @@ describe('Component: UiSuggest', () => {
27622802
});
27632803
}));
27642804

2805+
it('should render the compact summary using the provided custom template', () => {
2806+
component.items = generateSuggetionItemList('random');
2807+
2808+
const selectedValues = faker.helpers.shuffle(component.items!).slice(0, 3);
2809+
component.value = selectedValues;
2810+
2811+
component.multiple = true;
2812+
component.compact = true;
2813+
component.searchable = true;
2814+
2815+
fixture.detectChanges();
2816+
2817+
const displayContainer = fixture.debugElement.query(By.css('.display-container'));
2818+
const displayValue = displayContainer.query(By.css('.custom-display-value'));
2819+
const summaryText = displayValue.nativeElement.innerText.trim();
2820+
2821+
expect(summaryText).toBe(`${selectedValues[0].text} (+2 others)`);
2822+
});
2823+
27652824
[false, true].forEach((multiple) => {
27662825
[false, true].forEach((displayTemplateValue) => {
27672826
it(`should ${!multiple && displayTemplateValue ? '' : 'NOT'} render the displayed value with custom` +

projects/angular/components/ui-suggest/src/ui-suggest.component.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ export class UiSuggestComponent extends UiSuggestMatFormFieldDirective
309309
!this.isOpen &&
310310
this._hasValue
311311
) {
312-
return this.value.map(v => this.intl.translateLabel(v.text)).join(', ');
312+
return this._getValueSummary();
313313
}
314314

315315
return null;
@@ -506,6 +506,20 @@ export class UiSuggestComponent extends UiSuggestMatFormFieldDirective
506506
@Input()
507507
disableTooltip = false;
508508

509+
/**
510+
* Use compact summary info instead of chips
511+
*
512+
*/
513+
@Input()
514+
compact = false;
515+
516+
/**
517+
* The template to use for compact summary
518+
*
519+
*/
520+
@Input()
521+
compactSummaryTemplate?: TemplateRef<any>;
522+
509523
/**
510524
* Emits `once` when `data` is retrieved for the `first time`.
511525
*
@@ -1105,6 +1119,12 @@ export class UiSuggestComponent extends UiSuggestMatFormFieldDirective
11051119
}
11061120
}
11071121

1122+
computeDisplayValue() {
1123+
return this.value.length > 0
1124+
? this._getValueSummary()
1125+
: this.defaultValue;
1126+
}
1127+
11081128
private _selectActiveItem(closeAfterSelect: boolean) {
11091129
const item = this.headerItems![this.activeIndex] ?? this.items[this.activeIndex - this.headerItems!.length];
11101130
if (!item) { return; }
@@ -1385,4 +1405,8 @@ export class UiSuggestComponent extends UiSuggestMatFormFieldDirective
13851405
if (scenario) { throw new Error(errorText); }
13861406
});
13871407
}
1408+
1409+
private _getValueSummary() {
1410+
return this.value.map(v => this.intl.translateLabel(v.text)).join(', ');
1411+
}
13881412
}

projects/playground/src/app/pages/suggest/suggest.page.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,37 @@ <h2>Multiple select suggest</h2>
2424
[searchSourceFactory]="searchSourceFactory">
2525
</ui-suggest>
2626
</mat-form-field>
27+
28+
<h2>Multiple select with compact summary instead of chips</h2>
29+
<h3>With the default template</h3>
30+
<mat-form-field>
31+
<ui-suggest [searchable]="true"
32+
[disabled]="false"
33+
[multiple]="true"
34+
[enableCustomValue]="true"
35+
[searchSourceFactory]="searchSourceFactory"
36+
[compact]="true">
37+
</ui-suggest>
38+
</mat-form-field>
39+
<h3>With custom template</h3>
40+
<mat-form-field>
41+
<ui-suggest [searchable]="true"
42+
[disabled]="false"
43+
[multiple]="true"
44+
[enableCustomValue]="true"
45+
[searchSourceFactory]="searchSourceFactory"
46+
[compact]="true"
47+
[compactSummaryTemplate]="compactSummaryTemplate">
48+
</ui-suggest>
49+
50+
<ng-template #compactSummaryTemplate
51+
let-value>
52+
<div class="flex">
53+
<span class="text-ellipsis">{{value?.[0]?.text}}</span>
54+
<span *ngIf="(value?.length || 0) > 1"
55+
class="example-additional-selection">
56+
(+{{(value?.length || 0) - 1}} {{value?.length === 2 ? 'other' : 'others'}})
57+
</span>
58+
</div>
59+
</ng-template>
60+
</mat-form-field>

0 commit comments

Comments
 (0)