From 995f13e4b35c07eab2b17da9ac9326f23811043b Mon Sep 17 00:00:00 2001
From: Daniel Shuy <17351764+daniel-shuy@users.noreply.github.com>
Date: Wed, 21 Jan 2026 15:59:05 +0800
Subject: [PATCH] angular-material: Translate enum for
AutocompleteControlRenderer
---
.../library/controls/autocomplete.renderer.ts | 64 +++++++++++++++----
.../test/autocomplete-control.spec.ts | 54 ++++++++++++++--
2 files changed, 99 insertions(+), 19 deletions(-)
diff --git a/packages/angular-material/src/library/controls/autocomplete.renderer.ts b/packages/angular-material/src/library/controls/autocomplete.renderer.ts
index 596af746f..a138820d7 100644
--- a/packages/angular-material/src/library/controls/autocomplete.renderer.ts
+++ b/packages/angular-material/src/library/controls/autocomplete.renderer.ts
@@ -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';
@@ -89,12 +94,13 @@ import { MatAutocompleteModule } from '@angular/material/autocomplete';
autoActiveFirstOption
#auto="matAutocomplete"
(optionSelected)="onSelect($event)"
+ [displayWith]="displayFn"
>
- {{ option }}
+ {{ option.label }}
{{
@@ -127,14 +133,22 @@ export class AutocompleteControlRenderer
extends JsonFormsControl
implements OnInit
{
- @Input() options: string[];
- filteredOptions: Observable;
+ @Input() options?: EnumOption[] | string[];
+ translatedOptions?: EnumOption[];
+ filteredOptions: Observable;
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() {
@@ -146,6 +160,10 @@ export class AutocompleteControlRenderer
);
}
+ mapAdditionalProps(_props: StatePropsOfControl & OwnPropsOfEnum) {
+ this.translatedOptions = _props.options;
+ }
+
updateFilter(event: any) {
// ENTER
if (event.keyCode === 13) {
@@ -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);
diff --git a/packages/angular-material/test/autocomplete-control.spec.ts b/packages/angular-material/test/autocomplete-control.spec.ts
index 53a0bd645..5fd2acc54 100644
--- a/packages/angular-material/test/autocomplete-control.spec.ts
+++ b/packages/angular-material/test/autocomplete-control.spec.ts
@@ -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';
@@ -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);
}));
@@ -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', () => {
@@ -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 });
@@ -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 });
@@ -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', () => {
@@ -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;