Skip to content
Open
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 @@ -34,10 +34,15 @@ import {
Actions,
composeWithUi,
ControlElement,
EnumOption,
isEnumControl,
JsonFormsState,
mapStateToEnumControlProps,
OwnPropsOfControl,
OwnPropsOfEnum,
RankedTester,
rankWith,
StatePropsOfControl,
} from '@jsonforms/core';
import type { Observable } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
Expand Down Expand Up @@ -89,12 +94,13 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete';
autoActiveFirstOption
#auto="matAutocomplete"
(optionSelected)="onSelect($event)"
[displayWith]="displayFn"
>
<mat-option
*ngFor="let option of filteredOptions | async"
[value]="option"
[value]="option.value"
>
{{ option }}
{{ option.label }}
</mat-option>
</mat-autocomplete>
<mat-hint *ngIf="shouldShowUnfocusedDescription() || focused">{{
Expand Down Expand Up @@ -127,14 +133,22 @@ export class AutocompleteControlRenderer
extends JsonFormsControl
implements OnInit
{
@Input() options: string[];
filteredOptions: Observable<string[]>;
@Input() options?: EnumOption[] | string[];
translatedOptions?: EnumOption[];
filteredOptions: Observable<EnumOption[]>;
shouldFilter: boolean;
focused = false;

constructor(jsonformsService: JsonFormsAngularService) {
super(jsonformsService);
}

protected mapToProps(
state: JsonFormsState
): StatePropsOfControl & OwnPropsOfEnum {
return mapStateToEnumControlProps(state, this.getOwnProps());
}

getEventValue = (event: any) => event.target.value;

ngOnInit() {
Expand All @@ -146,6 +160,10 @@ export class AutocompleteControlRenderer
);
}

mapAdditionalProps(_props: StatePropsOfControl & OwnPropsOfEnum) {
this.translatedOptions = _props.options;
}

updateFilter(event: any) {
// ENTER
if (event.keyCode === 13) {
Expand All @@ -164,24 +182,44 @@ export class AutocompleteControlRenderer
this.triggerValidation();
}

filter(val: string): string[] {
return (this.options || this.scopedSchema.enum || []).filter(
displayFn(option: EnumOption): string {
return option.label;
}

filter(val: string): EnumOption[] {
return (this.translatedOptions || []).filter(
(option) =>
!this.shouldFilter ||
!val ||
option.toLowerCase().indexOf(val.toLowerCase()) === 0
option.label.toLowerCase().indexOf(val.toLowerCase()) === 0
);
}
protected getOwnProps(): OwnPropsOfAutoComplete {
protected getOwnProps(): OwnPropsOfControl & OwnPropsOfEnum {
return {
...super.getOwnProps(),
options: this.options,
options: this.stringOptionsToEnumOptions(this.options),
};
}
}

export const enumControlTester: RankedTester = rankWith(2, isEnumControl);
/**
* For {@link options} input backwards compatibility
*/
protected stringOptionsToEnumOptions(
options: typeof this.options
): EnumOption[] | undefined {
if (!options) {
return undefined;
}

interface OwnPropsOfAutoComplete extends OwnPropsOfControl {
options: string[];
return options.every((item) => typeof item === 'string')
? options.map((str) => {
return {
label: str,
value: str,
} satisfies EnumOption;
})
: options;
}
}

export const enumControlTester: RankedTester = rankWith(2, isEnumControl);
54 changes: 48 additions & 6 deletions packages/angular-material/test/autocomplete-control.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ import {
setupMockStore,
getJsonFormsService,
} from './common';
import { ControlElement, JsonSchema, Actions } from '@jsonforms/core';
import {
ControlElement,
JsonSchema,
Actions,
JsonFormsCore,
} from '@jsonforms/core';
import { AutocompleteControlRenderer } from '../src';
import { JsonFormsAngularService } from '@jsonforms/angular';
import { ErrorObject } from 'ajv';
Expand Down Expand Up @@ -108,7 +113,6 @@ describe('Autocomplete control Base Tests', () => {
fixture.detectChanges();
tick();
expect(component.data).toBe('A');
expect(inputElement.value).toBe('A');
expect(inputElement.disabled).toBe(false);
}));

Expand All @@ -124,7 +128,6 @@ describe('Autocomplete control Base Tests', () => {
tick();
fixture.detectChanges();
expect(component.data).toBe('B');
expect(inputElement.value).toBe('B');
}));

it('should update with undefined value', () => {
Expand All @@ -140,7 +143,6 @@ describe('Autocomplete control Base Tests', () => {
);
fixture.detectChanges();
expect(component.data).toBe(undefined);
expect(inputElement.value).toBe('');
});
it('should update with null value', () => {
setupMockStore(fixture, { uischema, schema, data });
Expand All @@ -155,7 +157,6 @@ describe('Autocomplete control Base Tests', () => {
);
fixture.detectChanges();
expect(component.data).toBe(null);
expect(inputElement.value).toBe('');
});
it('should not update with wrong ref', fakeAsync(() => {
setupMockStore(fixture, { uischema, schema, data });
Expand All @@ -170,7 +171,6 @@ describe('Autocomplete control Base Tests', () => {
fixture.detectChanges();
tick();
expect(component.data).toBe('A');
expect(inputElement.value).toBe('A');
}));
// store needed as we evaluate the calculated enabled value to disable/enable the control
it('can be disabled', () => {
Expand Down Expand Up @@ -275,6 +275,48 @@ describe('AutoComplete control Input Event Tests', () => {
.args[0] as MatAutocompleteSelectedEvent;
expect(event.option.value).toBe('Y');
}));
it('should render translated enum correctly', fakeAsync(async () => {
setupMockStore(fixture, { uischema, schema, data });
const state: JsonFormsCore = {
data,
schema,
uischema,
};
getJsonFormsService(component).init({
core: state,
i18n: {
translate: (key, defaultMessage) => {
const translations: { [key: string]: string } = {
'foo.A': 'Translated A',
'foo.B': 'Translated B',
'foo.C': 'Translated C',
};
return translations[key] ?? defaultMessage;
},
},
});
getJsonFormsService(component).updateCore(
Actions.init(data, schema, uischema)
);
component.ngOnInit();
fixture.detectChanges();
const spy = spyOn(component, 'onSelect');

await (await loader.getHarness(MatAutocompleteHarness)).focus();
fixture.detectChanges();

await (
await loader.getHarness(MatAutocompleteHarness)
).selectOption({
text: 'Translated B',
});
fixture.detectChanges();
tick();

const event = spy.calls.mostRecent()
.args[0] as MatAutocompleteSelectedEvent;
expect(event.option.value).toBe('B');
}));
});
describe('AutoComplete control Error Tests', () => {
let fixture: ComponentFixture<AutocompleteControlRenderer>;
Expand Down