AAE-46392 Fix card view select item crash on numeric initial value (#11917)

Guard autocomplete-only paths and coerce filter text to string so
select properties with numeric values (e.g. priority) no longer throw.
This commit is contained in:
Michaela
2026-05-26 15:57:37 +00:00
committed by GitHub
parent e6ef0de7a8
commit 70f90c65a5
2 changed files with 81 additions and 8 deletions
@@ -276,7 +276,8 @@ describe('CardViewSelectItemComponent', () => {
}); });
it('should set initial value to autocompleteControl', () => { it('should set initial value to autocompleteControl', () => {
component.ngOnChanges({}); fixture.componentRef.setInput('property', component.property);
fixture.componentRef.setInput('editable', true);
fixture.detectChanges(); fixture.detectChanges();
expect(component.autocompleteControl.value).toBe('initial value'); expect(component.autocompleteControl.value).toBe('initial value');
@@ -393,6 +394,70 @@ describe('CardViewSelectItemComponent', () => {
}); });
}); });
describe('Numeric initial value', () => {
const priorityOptions = [
{ key: 0, label: 'PROCESS_EDITOR.PRIORITIES.NONE' },
{ key: 1, label: 'PROCESS_EDITOR.PRIORITIES.LOW' },
{ key: 2, label: 'PROCESS_EDITOR.PRIORITIES.MEDIUM' },
{ key: 3, label: 'PROCESS_EDITOR.PRIORITIES.HIGH' }
];
const createNumericProperty = (config: {
autocompleteBased: boolean;
value: number;
label?: string;
displayNoneOption?: boolean;
options?: { key: number; label: string }[];
}) =>
new CardViewSelectItemModel({
label: config.label ?? 'Priority',
value: config.value,
key: 'priority',
editable: true,
autocompleteBased: config.autocompleteBased,
displayNoneOption: config.displayNoneOption,
options$: of(config.options ?? priorityOptions)
});
const applyInputs = (property: CardViewSelectItemModel<number>) => {
fixture.componentRef.setInput('property', property);
fixture.componentRef.setInput('editable', true);
fixture.detectChanges();
};
it('should not throw when autocompleteBased and initial value is numeric', fakeAsync(() => {
const property = createNumericProperty({
autocompleteBased: true,
value: 1,
options: [
{ key: 1, label: 'Option 1' },
{ key: 2, label: 'Option 2' }
]
});
const filterOptionsSpy = spyOn<any>(component, 'filterOptions').and.callThrough();
applyInputs(property);
tick(50);
expect(filterOptionsSpy).toHaveBeenCalled();
}));
it('should not call filterOptions when autocompleteBased is false and initial value is numeric', () => {
const property = createNumericProperty({
autocompleteBased: false,
value: 0,
label: 'PROCESS_EDITOR.ELEMENT_PROPERTIES.PRIORITY',
displayNoneOption: false
});
const filterOptionsSpy = spyOn<any>(component, 'filterOptions');
applyInputs(property);
expect(component.property.value).toBe(0);
expect(filterOptionsSpy).not.toHaveBeenCalled();
});
});
describe('Multivalued select', () => { describe('Multivalued select', () => {
const multivaluedMockData = [ const multivaluedMockData = [
{ key: 'one', label: 'One' }, { key: 'one', label: 'One' },
@@ -91,7 +91,7 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
this.property.value = []; this.property.value = [];
} }
if (changes.property?.firstChange) { if (changes.property?.firstChange && this.property.autocompleteBased) {
this.autocompleteControl.valueChanges this.autocompleteControl.valueChanges
.pipe( .pipe(
filter((textInputValue) => textInputValue !== this.editedValue && textInputValue !== null && !Array.isArray(textInputValue)), filter((textInputValue) => textInputValue !== this.editedValue && textInputValue !== null && !Array.isArray(textInputValue)),
@@ -126,7 +126,10 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
}); });
this.list$ = this.getList(); this.list$ = this.getList();
this.autocompleteControl.setValue(this.property.value);
if (this.property.autocompleteBased) {
this.autocompleteControl.setValue(this.property.value);
}
} }
onFilterInputChange(value: string) { onFilterInputChange(value: string) {
@@ -199,16 +202,21 @@ export class CardViewSelectItemComponent extends BaseCardView<CardViewSelectItem
} }
private filterOptions() { private filterOptions() {
if (!this.property.autocompleteBased) {
return;
}
this.getOptions() this.getOptions()
.pipe( .pipe(
map((options) => map((options) => {
options.filter((option) => { const filterValue = String(this.editedValue ?? '').toLowerCase();
return options.filter((option) => {
const isSelected = this.property.multivalued const isSelected = this.property.multivalued
? this.property.value.some((val) => val === option.key) ? this.property.value.some((val) => val === option.key)
: this.property.value === option.key; : this.property.value === option.key;
return !isSelected && option.label.toLowerCase().includes(this.editedValue.toLowerCase()); return !isSelected && option.label.toLowerCase().includes(filterValue);
}) });
) })
) )
.pipe(take(1)) .pipe(take(1))
.subscribe((options: CardViewSelectItemOption<string | number>[]) => { .subscribe((options: CardViewSelectItemOption<string | number>[]) => {