mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
[MNT-25149] Card view select item multivalued property support (#11891)
* [MNT-25149] Card view select item multivalued property support * [MNT-25149] CR fixes * [MNT-25149] CR fixes * [MNT-25149] CR fixes
This commit is contained in:
+27
@@ -367,6 +367,33 @@ describe('PropertyGroupTranslatorService', () => {
|
|||||||
expect(cardViewProperty.value).toBe('two');
|
expect(cardViewProperty.value).toBe('two');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should translate property for type LIST constraint with multivalued property', () => {
|
||||||
|
const definition: Definition = {
|
||||||
|
properties: [
|
||||||
|
{
|
||||||
|
id: 'FAS:PLAGUE',
|
||||||
|
constraints: [
|
||||||
|
{
|
||||||
|
type: 'LIST',
|
||||||
|
parameters: {
|
||||||
|
allowedValues: ['one', 'two', 'three']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} as Constraint
|
||||||
|
]
|
||||||
|
};
|
||||||
|
property.dataType = 'd:text';
|
||||||
|
property.multiValued = true;
|
||||||
|
propertyValues = { 'FAS:PLAGUE': ['one', 'three'] };
|
||||||
|
const cardViewGroup = service.translateToCardViewGroups(propertyGroups, propertyValues, definition);
|
||||||
|
|
||||||
|
const cardViewProperty = cardViewGroup[0].properties[0] as CardViewSelectItemModel<string>;
|
||||||
|
expect(cardViewProperty instanceof CardViewSelectItemModel).toBeTruthy();
|
||||||
|
expect(cardViewProperty.multivalued).toBe(true);
|
||||||
|
expect(cardViewProperty.value).toEqual(['one', 'three']);
|
||||||
|
});
|
||||||
|
|
||||||
it('should translate content type properties into card items', () => {
|
it('should translate content type properties into card items', () => {
|
||||||
const propertyBase = {
|
const propertyBase = {
|
||||||
id: 'fk:brendonstare',
|
id: 'fk:brendonstare',
|
||||||
|
|||||||
+1
-1
@@ -120,7 +120,7 @@ export class PropertyGroupTranslatorService {
|
|||||||
|
|
||||||
if (this.isListOfValues(propertyDefinition.constraints)) {
|
if (this.isListOfValues(propertyDefinition.constraints)) {
|
||||||
const options = propertyDefinition.constraints[0].parameters.allowedValues.map((value) => ({ key: value, label: value }));
|
const options = propertyDefinition.constraints[0].parameters.allowedValues.map((value) => ({ key: value, label: value }));
|
||||||
const properties = Object.assign(propertyDefinition, { options$: of(options) });
|
const properties = Object.assign(propertyDefinition, { options$: of(options), multivalued: isMultiValued });
|
||||||
|
|
||||||
cardViewItemProperty = new CardViewSelectItemModel(properties);
|
cardViewItemProperty = new CardViewSelectItemModel(properties);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+56
-19
@@ -13,7 +13,8 @@
|
|||||||
>{{ property.label | translate }}
|
>{{ property.label | translate }}
|
||||||
</mat-label>
|
</mat-label>
|
||||||
<mat-select
|
<mat-select
|
||||||
[(ngModel)]="value"
|
[multiple]="property.multivalued"
|
||||||
|
[(ngModel)]="property.value"
|
||||||
[disabled]="isReadonlyProperty || !editable"
|
[disabled]="isReadonlyProperty || !editable"
|
||||||
[ngClass]="{ 'adf-property-readonly-value': isReadonlyProperty || !editable }"
|
[ngClass]="{ 'adf-property-readonly-value': isReadonlyProperty || !editable }"
|
||||||
panelClass="adf-select-filter"
|
panelClass="adf-select-filter"
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
(validated)="onValidation($event)"
|
(validated)="onValidation($event)"
|
||||||
>
|
>
|
||||||
<adf-select-filter-input *ngIf="showInputFilter" (change)="onFilterInputChange($event)" />
|
<adf-select-filter-input *ngIf="showInputFilter" (change)="onFilterInputChange($event)" />
|
||||||
<mat-option *ngIf="displayNoneOption">{{ 'CORE.CARDVIEW.NONE' | translate }}</mat-option>
|
<mat-option *ngIf="displayNoneOption && !property.multivalued">{{ 'CORE.CARDVIEW.NONE' | translate }}</mat-option>
|
||||||
<mat-option *ngFor="let option of list$ | async" [value]="option.key">
|
<mat-option *ngFor="let option of list$ | async" [value]="option.key">
|
||||||
{{ option.label | translate }}
|
{{ option.label | translate }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
@@ -50,24 +51,60 @@
|
|||||||
>
|
>
|
||||||
{{ property.label | translate }}
|
{{ property.label | translate }}
|
||||||
</mat-label>
|
</mat-label>
|
||||||
<input
|
@if (property.multivalued) {
|
||||||
matInput
|
<mat-chip-grid #chipGrid [attr.aria-label]="'CORE.CARDVIEW.SELECTED_VALUES' | translate">
|
||||||
[matAutocomplete]="auto"
|
@for (val of property.value; track $index) {
|
||||||
class="adf-property-value"
|
<mat-chip-row (removed)="removeChip(val)">
|
||||||
[ngClass]="{
|
{{getOptionLabel(val) | async}}
|
||||||
'adf-property-value-editable': isEditable,
|
@if (isEditable) {
|
||||||
'adf-property-readonly-value': isReadonlyProperty || !editable
|
<button matChipRemove [attr.aria-label]="'CORE.CARDVIEW.REMOVE' | translate: { value: getOptionLabel(val) | async }">
|
||||||
}"
|
<mat-icon
|
||||||
title="{{ property.label | translate }}"
|
adf-icon="close"
|
||||||
[placeholder]="property.default"
|
matChipRemove
|
||||||
[attr.aria-label]="property.label | translate"
|
/>
|
||||||
[formControl]="autocompleteControl"
|
</button>
|
||||||
[title]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
}
|
||||||
[attr.data-automation-id]="'card-autocomplete-based-selectitem-value-' + property.key"
|
</mat-chip-row>
|
||||||
/>
|
}
|
||||||
<mat-autocomplete autoActiveFirstOption #auto="matAutocomplete" (optionSelected)="onOptionSelected($event)">
|
</mat-chip-grid>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matAutocomplete]="auto"
|
||||||
|
[matChipInputFor]="chipGrid"
|
||||||
|
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
|
||||||
|
(matChipInputTokenEnd)="addValueToList($event)"
|
||||||
|
class="adf-property-value"
|
||||||
|
[ngClass]="{
|
||||||
|
'adf-property-value-editable': isEditable,
|
||||||
|
'adf-property-readonly-value': isReadonlyProperty || !editable
|
||||||
|
}"
|
||||||
|
title="{{ property.label | translate }}"
|
||||||
|
[placeholder]="property.default"
|
||||||
|
[attr.aria-label]="property.label | translate"
|
||||||
|
[formControl]="autocompleteControl"
|
||||||
|
[title]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
||||||
|
[attr.data-automation-id]="'card-autocomplete-based-selectitem-value-' + property.key"
|
||||||
|
/>
|
||||||
|
} @else {
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
[matAutocomplete]="auto"
|
||||||
|
class="adf-property-value"
|
||||||
|
[ngClass]="{
|
||||||
|
'adf-property-value-editable': isEditable,
|
||||||
|
'adf-property-readonly-value': isReadonlyProperty || !editable
|
||||||
|
}"
|
||||||
|
title="{{ property.label | translate }}"
|
||||||
|
[placeholder]="property.default"
|
||||||
|
[attr.aria-label]="property.label | translate"
|
||||||
|
[formControl]="autocompleteControl"
|
||||||
|
[title]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
|
||||||
|
[attr.data-automation-id]="'card-autocomplete-based-selectitem-value-' + property.key"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="onOptionSelected($event)">
|
||||||
<mat-option
|
<mat-option
|
||||||
*ngFor="let option of property.options$ | async"
|
*ngFor="let option of filteredOptions"
|
||||||
[value]="option.key"
|
[value]="option.key"
|
||||||
[attr.data-automation-id]="'card-autocomplete-based-selectitem-option-' + property.key"
|
[attr.data-automation-id]="'card-autocomplete-based-selectitem-option-' + property.key"
|
||||||
>
|
>
|
||||||
|
|||||||
+311
-11
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||||
import { CardViewSelectItemModel } from '../../models/card-view-selectitem.model';
|
import { CardViewSelectItemModel } from '../../models/card-view-selectitem.model';
|
||||||
import { CardViewSelectItemComponent } from './card-view-selectitem.component';
|
import { CardViewSelectItemComponent } from './card-view-selectitem.component';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
@@ -25,7 +25,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
|||||||
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
|
||||||
import { UnitTestingUtils } from '../../../testing/unit-testing-utils';
|
import { UnitTestingUtils } from '../../../testing/unit-testing-utils';
|
||||||
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
import { CardViewUpdateService } from '../../services/card-view-update.service';
|
||||||
import { DebugElement } from '@angular/core';
|
import { DebugElement, SimpleChange, SimpleChanges } from '@angular/core';
|
||||||
import { CardViewPropertyValidatorDirective } from '../../directives/card-view-property-validator.directive';
|
import { CardViewPropertyValidatorDirective } from '../../directives/card-view-property-validator.directive';
|
||||||
import { MatError } from '@angular/material/form-field';
|
import { MatError } from '@angular/material/form-field';
|
||||||
import { FormControl, NgModel } from '@angular/forms';
|
import { FormControl, NgModel } from '@angular/forms';
|
||||||
@@ -114,14 +114,14 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
component.ngOnChanges({});
|
component.ngOnChanges({});
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(component.value).toEqual('two');
|
expect(component.property.value).toEqual('two');
|
||||||
expect(component.isEditable).toBe(true);
|
expect(component.isEditable).toBe(true);
|
||||||
|
|
||||||
const options = await testingUtils.getMatSelectOptions();
|
const options = await testingUtils.getMatSelectOptions();
|
||||||
expect(options.length).toEqual(4);
|
expect(options.length).toEqual(4);
|
||||||
await options[1].click();
|
await options[1].click();
|
||||||
|
|
||||||
expect(component.value).toEqual('one');
|
expect(component.property.value).toEqual('one');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be possible edit selectBox item with numbers', async () => {
|
it('should be possible edit selectBox item with numbers', async () => {
|
||||||
@@ -134,7 +134,7 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
component.ngOnChanges({});
|
component.ngOnChanges({});
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(component.value).toEqual(2);
|
expect(component.property.value).toEqual(2);
|
||||||
expect(component.isEditable).toBe(true);
|
expect(component.isEditable).toBe(true);
|
||||||
|
|
||||||
const options = await testingUtils.getMatSelectOptions();
|
const options = await testingUtils.getMatSelectOptions();
|
||||||
@@ -142,7 +142,7 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
expect(options.length).toEqual(4);
|
expect(options.length).toEqual(4);
|
||||||
await options[1].click();
|
await options[1].click();
|
||||||
|
|
||||||
expect(component.value).toEqual(1);
|
expect(component.property.value).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to enable None option', async () => {
|
it('should be able to enable None option', async () => {
|
||||||
@@ -174,11 +174,21 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
component.ngOnChanges({});
|
component.ngOnChanges({});
|
||||||
component.editable = true;
|
component.editable = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const not_editable_label = fixture.nativeElement.querySelector('.adf-property-label-not-editable');
|
const not_editable_label = testingUtils.getByCSS('.adf-property-label-not-editable');
|
||||||
const field = await testingUtils.getMatFormFieldByCSS('.adf-property-value');
|
const field = await testingUtils.getMatFormFieldByCSS('.adf-property-value');
|
||||||
|
|
||||||
expect(await field.hasLabel()).toBeTrue();
|
expect(await field.hasLabel()).toBeTrue();
|
||||||
expect(not_editable_label).toBeNull();
|
expect(not_editable_label).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have proper aria-label on select box', async () => {
|
||||||
|
component.ngOnChanges({});
|
||||||
|
component.editable = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const selectBox = await testingUtils.getMatSelectByDataAutomationId('select-box');
|
||||||
|
const host = await selectBox.host();
|
||||||
|
expect(await host.getAttribute('aria-label')).toBe('Select box label');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -276,7 +286,7 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
const autocompleteValueSpy = spyOn(cardViewUpdateService.autocompleteInputValue$, 'next');
|
const autocompleteValueSpy = spyOn(cardViewUpdateService.autocompleteInputValue$, 'next');
|
||||||
component.editedValue = '';
|
component.editedValue = '';
|
||||||
component.editable = true;
|
component.editable = true;
|
||||||
component.ngOnChanges({ property: { firstChange: true } } as any);
|
component.ngOnChanges({ property: new SimpleChange(null, component.property, true) } as SimpleChanges);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
component.autocompleteControl.setValue('new value');
|
component.autocompleteControl.setValue('new value');
|
||||||
@@ -288,7 +298,11 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
|
|
||||||
it('should update value correctly on option selected', () => {
|
it('should update value correctly on option selected', () => {
|
||||||
cardViewUpdateService.update = jasmine.createSpy('update');
|
cardViewUpdateService.update = jasmine.createSpy('update');
|
||||||
const event: MatAutocompleteSelectedEvent = {
|
component.filteredOptions = [
|
||||||
|
{ key: '1', label: 'Option 1' },
|
||||||
|
{ key: '2', label: 'Option 2' }
|
||||||
|
];
|
||||||
|
const event = {
|
||||||
option: {
|
option: {
|
||||||
value: '1'
|
value: '1'
|
||||||
}
|
}
|
||||||
@@ -300,7 +314,7 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
component.onOptionSelected(event);
|
component.onOptionSelected(event);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(component.autocompleteControl.value).toBe('Option 1');
|
expect(component.property.value).toBe('1');
|
||||||
expect(cardViewUpdateService.update).toHaveBeenCalledWith(jasmine.objectContaining(component.property), '1');
|
expect(cardViewUpdateService.update).toHaveBeenCalledWith(jasmine.objectContaining(component.property), '1');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -319,6 +333,10 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should populate options for autocomplete', async () => {
|
it('should populate options for autocomplete', async () => {
|
||||||
|
component.filteredOptions = [
|
||||||
|
{ key: '1', label: 'Option 1' },
|
||||||
|
{ key: '2', label: 'Option 2' }
|
||||||
|
];
|
||||||
component.ngOnChanges({});
|
component.ngOnChanges({});
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@@ -327,6 +345,288 @@ describe('CardViewSelectItemComponent', () => {
|
|||||||
expect(await options[0].getText()).toContain('Option 1');
|
expect(await options[0].getText()).toContain('Option 1');
|
||||||
expect(await options[1].getText()).toContain('Option 2');
|
expect(await options[1].getText()).toContain('Option 2');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should update filteredOptions when autocompleteControl value changes', fakeAsync(() => {
|
||||||
|
component.editedValue = '';
|
||||||
|
component.editable = true;
|
||||||
|
component.ngOnChanges({ property: new SimpleChange(null, component.property, true) } as SimpleChanges);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.autocompleteControl.setValue('Option 1');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
tick(50);
|
||||||
|
|
||||||
|
expect(component.filteredOptions.length).toBe(1);
|
||||||
|
expect(component.filteredOptions[0].label).toBe('Option 1');
|
||||||
|
|
||||||
|
component.autocompleteControl.setValue('2');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
tick(50);
|
||||||
|
|
||||||
|
expect(component.filteredOptions.length).toBe(1);
|
||||||
|
expect(component.filteredOptions[0].label).toBe('Option 2');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should render autocomplete options from filteredOptions array', async () => {
|
||||||
|
component.filteredOptions = [
|
||||||
|
{ key: '1', label: 'Option 1' },
|
||||||
|
{ key: '2', label: 'Option 2' }
|
||||||
|
];
|
||||||
|
component.ngOnChanges({});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const options = await testingUtils.typeAndGetOptionsForMatAutoComplete(fixture, 'Option');
|
||||||
|
|
||||||
|
expect(options.length).toBe(2);
|
||||||
|
expect(await options[0].getText()).toContain('Option 1');
|
||||||
|
expect(await options[1].getText()).toContain('Option 2');
|
||||||
|
});
|
||||||
|
it('should have proper aria-label on autocomplete input', () => {
|
||||||
|
component.ngOnChanges({});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const input = testingUtils.getInputByCSS('.adf-property-value');
|
||||||
|
expect(input).toBeTruthy();
|
||||||
|
expect(input.getAttribute('aria-label')).toBe('Test Label');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Multivalued select', () => {
|
||||||
|
const multivaluedMockData = [
|
||||||
|
{ key: 'one', label: 'One' },
|
||||||
|
{ key: 'two', label: 'Two' },
|
||||||
|
{ key: 'three', label: 'Three' }
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component.property = new CardViewSelectItemModel({
|
||||||
|
label: 'Multi Select Label',
|
||||||
|
value: ['one'],
|
||||||
|
key: 'multi-key',
|
||||||
|
editable: true,
|
||||||
|
multivalued: true,
|
||||||
|
options$: of(multivaluedMockData)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should initialize property.value as empty array if not set and multivalued is true', () => {
|
||||||
|
component.property.value = null;
|
||||||
|
component.ngOnChanges({});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component.property.value).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enable multiple selection on select box when multivalued is true', async () => {
|
||||||
|
component.editable = true;
|
||||||
|
component.displayNoneOption = false;
|
||||||
|
component.ngOnChanges({});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const selectBox = await testingUtils.getMatSelectByDataAutomationId('select-box');
|
||||||
|
expect(await selectBox.isMultiple()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not display None option when multivalued is true', async () => {
|
||||||
|
component.editable = true;
|
||||||
|
component.displayNoneOption = true;
|
||||||
|
component.ngOnChanges({});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const options = await testingUtils.getMatSelectOptions();
|
||||||
|
const optionTexts = await Promise.all(options.map((opt) => opt.getText()));
|
||||||
|
const hasNoneOption = optionTexts.some((text) => text.includes('CORE.CARDVIEW.NONE'));
|
||||||
|
|
||||||
|
expect(hasNoneOption).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow selecting multiple values', async () => {
|
||||||
|
component.editable = true;
|
||||||
|
component.displayNoneOption = false;
|
||||||
|
component.ngOnChanges({});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component.property.value).toEqual(['one']);
|
||||||
|
|
||||||
|
const options = await testingUtils.getMatSelectOptions();
|
||||||
|
await options[1].click();
|
||||||
|
|
||||||
|
expect(component.property.value).toContain('two');
|
||||||
|
expect(component.property.value.length).toBe(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Multivalued autocomplete based', () => {
|
||||||
|
const multivaluedOptions = [
|
||||||
|
{ key: 'option1', label: 'Option 1' },
|
||||||
|
{ key: 'option2', label: 'Option 2' },
|
||||||
|
{ key: 'option3', label: 'Option 3' }
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
component.property = new CardViewSelectItemModel({
|
||||||
|
label: 'Multi Autocomplete Label',
|
||||||
|
value: [],
|
||||||
|
key: 'multi-autocomplete-key',
|
||||||
|
editable: true,
|
||||||
|
autocompleteBased: true,
|
||||||
|
multivalued: true,
|
||||||
|
options$: of(multivaluedOptions)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add value to array when option is selected', async () => {
|
||||||
|
component.editable = true;
|
||||||
|
component.filteredOptions = multivaluedOptions;
|
||||||
|
component.ngOnChanges({ property: new SimpleChange(null, component.property, true) } as SimpleChanges);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const options = await testingUtils.typeAndGetOptionsForMatAutoComplete(fixture, 'Option');
|
||||||
|
|
||||||
|
expect(options.length).toBe(3);
|
||||||
|
|
||||||
|
await options[0].click();
|
||||||
|
|
||||||
|
expect(component.property.value).toContain('option1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render chip remove buttons with proper aria-label', () => {
|
||||||
|
component.property.value = ['option1'];
|
||||||
|
component.editable = true;
|
||||||
|
component.ngOnChanges({});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const removeButtons = testingUtils.getAllByCSS('button[aria-label*="CORE.CARDVIEW.REMOVE"]');
|
||||||
|
expect(removeButtons.length).toBeGreaterThan(0);
|
||||||
|
expect(removeButtons[0].nativeElement.getAttribute('aria-label')).toContain('CORE.CARDVIEW.REMOVE');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove chip when remove button is clicked', async () => {
|
||||||
|
const updateSpy = spyOn(cardViewUpdateService, 'update');
|
||||||
|
component.property.value = ['option1', 'option2'];
|
||||||
|
component.editable = true;
|
||||||
|
component.ngOnChanges({});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const removeButtons = testingUtils.getAllByCSS('button[matChipRemove]');
|
||||||
|
expect(removeButtons.length).toBe(2);
|
||||||
|
|
||||||
|
removeButtons[0].nativeElement.click();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component.property.value).toEqual(['option2']);
|
||||||
|
expect(updateSpy).toHaveBeenCalledWith(jasmine.objectContaining(component.property), ['option2']);
|
||||||
|
|
||||||
|
const remainingChips = await testingUtils.getMatChips();
|
||||||
|
expect(remainingChips.length).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add value to list when pressing ENTER with valid option', fakeAsync(() => {
|
||||||
|
component.property.value = ['option1'];
|
||||||
|
component.filteredOptions = [
|
||||||
|
{ key: 'option2', label: 'option2' },
|
||||||
|
{ key: 'option3', label: 'option3' }
|
||||||
|
];
|
||||||
|
component.editable = true;
|
||||||
|
component.ngOnInit();
|
||||||
|
component.ngOnChanges({ property: new SimpleChange(null, component.property, true) } as SimpleChanges);
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick(50);
|
||||||
|
|
||||||
|
const updateSpy = spyOn(cardViewUpdateService, 'update');
|
||||||
|
|
||||||
|
const chipInputEvent = { value: 'option2', chipInput: { clear: jasmine.createSpy('clear') } } as any;
|
||||||
|
component.addValueToList(chipInputEvent);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick(50);
|
||||||
|
|
||||||
|
expect(component.property.value).toContain('option2');
|
||||||
|
expect(updateSpy).toHaveBeenCalledWith(jasmine.objectContaining(component.property), ['option1', 'option2']);
|
||||||
|
expect(chipInputEvent.chipInput.clear).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not add value when pressing ENTER with invalid option', fakeAsync(() => {
|
||||||
|
component.property.value = ['option1'];
|
||||||
|
component.filteredOptions = [
|
||||||
|
{ key: 'option2', label: 'option2' },
|
||||||
|
{ key: 'option3', label: 'option3' }
|
||||||
|
];
|
||||||
|
component.editable = true;
|
||||||
|
component.ngOnInit();
|
||||||
|
component.ngOnChanges({ property: new SimpleChange(null, component.property, true) } as SimpleChanges);
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick(50);
|
||||||
|
|
||||||
|
const updateSpy = spyOn(cardViewUpdateService, 'update');
|
||||||
|
|
||||||
|
const chipInputEvent = { value: 'invalidOption', chipInput: { clear: jasmine.createSpy('clear') } } as any;
|
||||||
|
component.addValueToList(chipInputEvent);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick(50);
|
||||||
|
|
||||||
|
expect(component.property.value).toEqual(['option1']);
|
||||||
|
expect(updateSpy).not.toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should filter out already selected options from filteredOptions', fakeAsync(() => {
|
||||||
|
component.filteredOptions = [...multivaluedOptions];
|
||||||
|
component.property.value = ['option1'];
|
||||||
|
component.editedValue = '';
|
||||||
|
component.editable = true;
|
||||||
|
component.ngOnInit();
|
||||||
|
component.ngOnChanges({ property: new SimpleChange(null, component.property, true) } as SimpleChanges);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.autocompleteControl.setValue('Op');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
tick(50);
|
||||||
|
|
||||||
|
expect(component.filteredOptions.length).toBe(2);
|
||||||
|
expect(component.filteredOptions[0].label).toBe('Option 2');
|
||||||
|
expect(component.filteredOptions[1].label).toBe('Option 3');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should update filteredOptions after option selection', fakeAsync(() => {
|
||||||
|
component.filteredOptions = [...multivaluedOptions];
|
||||||
|
component.editable = true;
|
||||||
|
component.ngOnChanges({ property: new SimpleChange(null, component.property, true) } as SimpleChanges);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const event = {
|
||||||
|
option: {
|
||||||
|
value: 'option1'
|
||||||
|
}
|
||||||
|
} as MatAutocompleteSelectedEvent;
|
||||||
|
|
||||||
|
component.onOptionSelected(event);
|
||||||
|
tick(50);
|
||||||
|
|
||||||
|
expect(component.property.value).toContain('option1');
|
||||||
|
expect(component.filteredOptions.length).toBe(2);
|
||||||
|
expect(component.filteredOptions[0].label).toBe('Option 2');
|
||||||
|
expect(component.filteredOptions[1].label).toBe('Option 3');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should update filteredOptions after chip removal', fakeAsync(() => {
|
||||||
|
component.property.value = ['option1', 'option2'];
|
||||||
|
component.filteredOptions = [multivaluedOptions[2]];
|
||||||
|
component.editable = true;
|
||||||
|
component.ngOnChanges({ property: new SimpleChange(null, component.property, true) } as SimpleChanges);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.removeChip('option1');
|
||||||
|
tick(100);
|
||||||
|
|
||||||
|
expect(component.property.value).not.toContain('option1');
|
||||||
|
expect(component.filteredOptions.length).toBe(2);
|
||||||
|
expect(component.filteredOptions[0].label).toBe('Option 1');
|
||||||
|
expect(component.filteredOptions[1].label).toBe('Option 3');
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Validation', () => {
|
describe('Validation', () => {
|
||||||
|
|||||||
+72
-19
@@ -22,7 +22,7 @@ import { CardViewSelectItemOption } from '../../interfaces/card-view.interfaces'
|
|||||||
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
|
import { MatSelectChange, MatSelectModule } from '@angular/material/select';
|
||||||
import { BaseCardView } from '../base-card-view';
|
import { BaseCardView } from '../base-card-view';
|
||||||
import { AppConfigService } from '../../../app-config/app-config.service';
|
import { AppConfigService } from '../../../app-config/app-config.service';
|
||||||
import { map, debounceTime, filter, first } from 'rxjs/operators';
|
import { map, debounceTime, filter, take } from 'rxjs/operators';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { TranslatePipe } from '@ngx-translate/core';
|
import { TranslatePipe } from '@ngx-translate/core';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
@@ -32,6 +32,9 @@ import { MatInputModule } from '@angular/material/input';
|
|||||||
import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule, UntypedFormControl } from '@angular/forms';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { CardViewPropertyValidatorDirective } from '../../directives/card-view-property-validator.directive';
|
import { CardViewPropertyValidatorDirective } from '../../directives/card-view-property-validator.directive';
|
||||||
|
import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips';
|
||||||
|
import { ENTER } from '@angular/cdk/keycodes';
|
||||||
|
import { IconModule } from '../../../icon/icon.module';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-card-view-selectitem',
|
selector: 'adf-card-view-selectitem',
|
||||||
@@ -45,7 +48,9 @@ import { CardViewPropertyValidatorDirective } from '../../directives/card-view-p
|
|||||||
MatInputModule,
|
MatInputModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
CardViewPropertyValidatorDirective,
|
CardViewPropertyValidatorDirective,
|
||||||
FormsModule
|
FormsModule,
|
||||||
|
MatChipsModule,
|
||||||
|
IconModule
|
||||||
],
|
],
|
||||||
templateUrl: './card-view-selectitem.component.html',
|
templateUrl: './card-view-selectitem.component.html',
|
||||||
styleUrls: ['./card-view-selectitem.component.scss'],
|
styleUrls: ['./card-view-selectitem.component.scss'],
|
||||||
@@ -54,6 +59,7 @@ import { CardViewPropertyValidatorDirective } from '../../directives/card-view-p
|
|||||||
})
|
})
|
||||||
export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItemModel<string | number>> implements OnInit, OnChanges {
|
export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItemModel<string | number>> implements OnInit, OnChanges {
|
||||||
static HIDE_FILTER_LIMIT = 5;
|
static HIDE_FILTER_LIMIT = 5;
|
||||||
|
readonly separatorKeysCodes = [ENTER] as const;
|
||||||
|
|
||||||
@Input() options$: Observable<CardViewSelectItemOption<string | number>[]>;
|
@Input() options$: Observable<CardViewSelectItemOption<string | number>[]>;
|
||||||
|
|
||||||
@@ -63,13 +69,13 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
|
|||||||
@Input()
|
@Input()
|
||||||
displayEmpty: boolean = true;
|
displayEmpty: boolean = true;
|
||||||
|
|
||||||
value: string | number;
|
|
||||||
filter$ = new BehaviorSubject<string>('');
|
filter$ = new BehaviorSubject<string>('');
|
||||||
showInputFilter: boolean = false;
|
showInputFilter: boolean = false;
|
||||||
list$: Observable<CardViewSelectItemOption<string | number>[]> = null;
|
list$: Observable<CardViewSelectItemOption<string | number>[]> = null;
|
||||||
templateType = '';
|
templateType = '';
|
||||||
autocompleteControl = new UntypedFormControl();
|
autocompleteControl = new UntypedFormControl();
|
||||||
editedValue: string | number;
|
editedValue = '';
|
||||||
|
filteredOptions: CardViewSelectItemOption<string | number>[] = [];
|
||||||
|
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
private readonly appConfig = inject(AppConfigService);
|
private readonly appConfig = inject(AppConfigService);
|
||||||
@@ -81,17 +87,21 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
this.value = this.property.value;
|
if (!this.property.value && this.property.multivalued) {
|
||||||
|
this.property.value = [];
|
||||||
|
}
|
||||||
|
|
||||||
if (changes.property?.firstChange) {
|
if (changes.property?.firstChange) {
|
||||||
this.autocompleteControl.valueChanges
|
this.autocompleteControl.valueChanges
|
||||||
.pipe(
|
.pipe(
|
||||||
filter((textInputValue) => textInputValue !== this.editedValue && textInputValue !== null),
|
filter((textInputValue) => textInputValue !== this.editedValue && textInputValue !== null && !Array.isArray(textInputValue)),
|
||||||
debounceTime(50),
|
debounceTime(50),
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
)
|
)
|
||||||
.subscribe((textInputValue) => {
|
.subscribe((textInputValue) => {
|
||||||
this.editedValue = textInputValue;
|
this.editedValue = textInputValue;
|
||||||
this.cardViewUpdateService.autocompleteInputValue$.next(textInputValue);
|
this.cardViewUpdateService.autocompleteInputValue$.next(textInputValue);
|
||||||
|
this.filterOptions();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +130,7 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
onFilterInputChange(value: string) {
|
onFilterInputChange(value: string) {
|
||||||
this.filter$.next(value.toString());
|
this.filter$.next(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getOptions(): Observable<CardViewSelectItemOption<string | number>[]> {
|
private getOptions(): Observable<CardViewSelectItemOption<string | number>[]> {
|
||||||
@@ -134,32 +144,75 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
onOptionSelected(event: MatAutocompleteSelectedEvent) {
|
onOptionSelected(event: MatAutocompleteSelectedEvent) {
|
||||||
this.getOptions()
|
const selectedOption = this.filteredOptions.find((option) => option.key === event.option.value);
|
||||||
.pipe(first())
|
if (selectedOption) {
|
||||||
.subscribe((options) => {
|
if (this.property.multivalued) {
|
||||||
const selectedOption = options.find((option) => option.key === event.option.value);
|
this.property.value.push(event.option.value);
|
||||||
if (selectedOption) {
|
} else {
|
||||||
this.autocompleteControl.setValue(selectedOption.label);
|
this.property.value = event.option.value;
|
||||||
this.cardViewUpdateService.update({ ...this.property } as CardViewSelectItemModel<string>, selectedOption.key);
|
this.autocompleteControl.setValue(selectedOption.label);
|
||||||
}
|
}
|
||||||
});
|
this.cardViewUpdateService.update(this.property, this.property.value);
|
||||||
|
this.filterOptions();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(event: MatSelectChange): void {
|
onChange(event: MatSelectChange): void {
|
||||||
const selectedOption = event.value !== undefined ? event.value : null;
|
const selectedOptions = event.value !== undefined ? event.value : null;
|
||||||
this.cardViewUpdateService.update({ ...this.property } as CardViewSelectItemModel<string>, selectedOption);
|
this.cardViewUpdateService.update(this.property, selectedOptions);
|
||||||
this.property.value = selectedOption;
|
this.property.value = selectedOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
onValidation(errors: string[]): void {
|
onValidation(errors: string[]): void {
|
||||||
this._error = errors.join('<br>');
|
this._error = errors.join('<br>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeChip(value: string | number) {
|
||||||
|
this.property.value = this.property.value.filter((v) => v !== value);
|
||||||
|
this.cardViewUpdateService.update(this.property, this.property.value);
|
||||||
|
this.filterOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
addValueToList(newListItem: MatChipInputEvent) {
|
||||||
|
const selectedOption = this.filteredOptions.find((option) => option.key === newListItem.value || option.label === newListItem.value);
|
||||||
|
if (selectedOption) {
|
||||||
|
this.property.value.push(selectedOption.key);
|
||||||
|
this.cardViewUpdateService.update(this.property, this.property.value);
|
||||||
|
newListItem.chipInput.clear();
|
||||||
|
this.filterOptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get showProperty(): boolean {
|
get showProperty(): boolean {
|
||||||
return this.displayEmpty || !this.property.isEmpty();
|
return this.displayEmpty || !this.property.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getOptionLabel(value: string | number): Observable<string> {
|
||||||
|
return this.getOptions().pipe(
|
||||||
|
take(1),
|
||||||
|
map((options) => options.find((option) => option.key === value)?.label)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private get optionsLimit(): number {
|
private get optionsLimit(): number {
|
||||||
return this.appConfig.get<number>('content-metadata.selectFilterLimit', CardViewSelectItemComponent.HIDE_FILTER_LIMIT);
|
return this.appConfig.get<number>('content-metadata.selectFilterLimit', CardViewSelectItemComponent.HIDE_FILTER_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private filterOptions() {
|
||||||
|
this.getOptions()
|
||||||
|
.pipe(
|
||||||
|
map((options) =>
|
||||||
|
options.filter((option) => {
|
||||||
|
const isSelected = this.property.multivalued
|
||||||
|
? this.property.value.some((val) => val === option.key)
|
||||||
|
: this.property.value === option.key;
|
||||||
|
return !isSelected && option.label.toLowerCase().includes(this.editedValue.toLowerCase());
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe((options: CardViewSelectItemOption<string | number>[]) => {
|
||||||
|
this.filteredOptions = options;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export interface CardViewSelectItemOption<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CardViewSelectItemProperties<T> extends CardViewItemProperties {
|
export interface CardViewSelectItemProperties<T> extends CardViewItemProperties {
|
||||||
value: string | number;
|
value: T | T[];
|
||||||
options$: Observable<CardViewSelectItemOption<T>[]>;
|
options$: Observable<CardViewSelectItemOption<T>[]>;
|
||||||
displayNoneOption?: boolean;
|
displayNoneOption?: boolean;
|
||||||
autocompleteBased?: boolean;
|
autocompleteBased?: boolean;
|
||||||
|
|||||||
@@ -72,5 +72,35 @@ describe('CardViewSelectItemModel', () => {
|
|||||||
const itemModel = new CardViewSelectItemModel(properties);
|
const itemModel = new CardViewSelectItemModel(properties);
|
||||||
expect(itemModel.autocompleteBased).toBe(true);
|
expect(itemModel.autocompleteBased).toBe(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should return comma-separated labels for multivalued array', (done) => {
|
||||||
|
properties.value = ['one', 'three'];
|
||||||
|
const itemModel = new CardViewSelectItemModel(properties);
|
||||||
|
|
||||||
|
itemModel.displayValue.subscribe((value) => {
|
||||||
|
expect(value).toBe('One, Three');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty string for multivalued array with no matching options', (done) => {
|
||||||
|
properties.value = ['nonexistent'];
|
||||||
|
const itemModel = new CardViewSelectItemModel(properties);
|
||||||
|
|
||||||
|
itemModel.displayValue.subscribe((value) => {
|
||||||
|
expect(value).toBe('');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty multivalued array', (done) => {
|
||||||
|
properties.value = [];
|
||||||
|
const itemModel = new CardViewSelectItemModel(properties);
|
||||||
|
|
||||||
|
itemModel.displayValue.subscribe((value) => {
|
||||||
|
expect(value).toBe('');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,8 +40,10 @@ export class CardViewSelectItemModel<T> extends CardViewBaseItemModel implements
|
|||||||
|
|
||||||
this.valueFetch$ = this.options$.pipe(
|
this.valueFetch$ = this.options$.pipe(
|
||||||
switchMap((options) => {
|
switchMap((options) => {
|
||||||
const option = options.find((o) => o.key === this.value?.toString());
|
if (Array.isArray(this.value)) {
|
||||||
return of(option ? option.label : '');
|
return of(this.value.map((v) => options.find((o) => o.key === v)?.label).join(', '));
|
||||||
|
}
|
||||||
|
return of(options.find((o) => o.key === this.value?.toString())?.label ?? '');
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,6 +255,8 @@
|
|||||||
"VALUE": "Value"
|
"VALUE": "Value"
|
||||||
},
|
},
|
||||||
"NONE": "None",
|
"NONE": "None",
|
||||||
|
"SELECTED_VALUES": "Selected values",
|
||||||
|
"REMOVE": "Remove {{ value }}",
|
||||||
"VALIDATORS": {
|
"VALIDATORS": {
|
||||||
"FLOAT_VALIDATION_ERROR": "Use a number format",
|
"FLOAT_VALIDATION_ERROR": "Use a number format",
|
||||||
"INT_VALIDATION_ERROR": "Use an integer format",
|
"INT_VALIDATION_ERROR": "Use an integer format",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { DebugElement, Type } from '@angular/core';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { MatSelectHarness } from '@angular/material/select/testing';
|
import { MatSelectHarness } from '@angular/material/select/testing';
|
||||||
import { MatOptionHarness } from '@angular/material/core/testing';
|
import { MatOptionHarness } from '@angular/material/core/testing';
|
||||||
import { MatChipGridHarness, MatChipHarness, MatChipListboxHarness } from '@angular/material/chips/testing';
|
import { MatChipGridHarness, MatChipHarness, MatChipInputHarness, MatChipListboxHarness } from '@angular/material/chips/testing';
|
||||||
import { MatButtonHarness } from '@angular/material/button/testing';
|
import { MatButtonHarness } from '@angular/material/button/testing';
|
||||||
import { MatIconHarness } from '@angular/material/icon/testing';
|
import { MatIconHarness } from '@angular/material/icon/testing';
|
||||||
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
|
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
|
||||||
@@ -344,6 +344,20 @@ export class UnitTestingUtils {
|
|||||||
return this.loader.hasHarness(MatChipGridHarness);
|
return this.loader.hasHarness(MatChipGridHarness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** MatChipInput related methods */
|
||||||
|
|
||||||
|
async getMatChipInput(): Promise<MatChipInputHarness> {
|
||||||
|
return this.loader.getHarness(MatChipInputHarness);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMatChipInputByDataAutomationId(dataAutomationId: string): Promise<MatChipInputHarness> {
|
||||||
|
return this.loader.getHarness(MatChipInputHarness.with({ selector: `[data-automation-id="${dataAutomationId}"]` }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMatChipInputByCSS(selector: string): Promise<MatChipInputHarness> {
|
||||||
|
return this.loader.getHarness(MatChipInputHarness.with({ selector }));
|
||||||
|
}
|
||||||
|
|
||||||
/** MatFromField related methods */
|
/** MatFromField related methods */
|
||||||
|
|
||||||
async getMatFormField(): Promise<MatFormFieldHarness> {
|
async getMatFormField(): Promise<MatFormFieldHarness> {
|
||||||
|
|||||||
Reference in New Issue
Block a user