Skip to content

Commit 98fa995

Browse files
spike-rabbitspliffone
authored andcommitted
feat(form): support Angular's signal forms with new si-form-field
Introduces a new component called `si-form-field` which is built around Angular's signal forms. This initial version only supports native browser inputs. Currently missing: - integration of the error resolution mechanism - label in a separate column - fieldset combination with fieldset - documentation
1 parent 9f1d66a commit 98fa995

17 files changed

Lines changed: 569 additions & 0 deletions

api-goldens/element-ng/form/index.api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { AfterContentChecked } from '@angular/core';
99
import { AfterContentInit } from '@angular/core';
1010
import * as _angular_core from '@angular/core';
1111
import * as _angular_forms from '@angular/forms';
12+
import * as _angular_forms_signals from '@angular/forms/signals';
1213
import { DoCheck } from '@angular/core';
1314
import { ElementRef } from '@angular/core';
1415
import { FormGroup } from '@angular/forms';
@@ -21,12 +22,16 @@ import { OnInit } from '@angular/core';
2122
import { Provider } from '@angular/core';
2223
import { RequiredValidator } from '@angular/forms';
2324
import { Signal } from '@angular/core';
25+
import { SignalFormsConfig } from '@angular/forms/signals';
2426
import { SimpleChanges } from '@angular/core';
2527
import { TranslatableString } from '@siemens/element-translate-ng/translate';
2628

2729
// @public
2830
export const provideFormValidationErrorMapper: (mapper: SiFormValidationErrorMapper) => Provider;
2931

32+
// @public
33+
export const provideSiFormFieldConfig: (config?: SignalFormsConfig) => Provider[];
34+
3035
// @public
3136
export const SI_FORM_ITEM_CONTROL: InjectionToken<SiFormItemControl>;
3237

@@ -47,6 +52,12 @@ export class SiFormContainerComponent<TControl extends {
4752
get validFormContainerMessage(): boolean;
4853
}
4954

55+
// @public
56+
export class SiFormFieldComponent {
57+
constructor();
58+
readonly label: _angular_core.InputSignal<TranslatableString>;
59+
}
60+
5061
// @public (undocumented)
5162
export class SiFormFieldsetComponent implements DoCheck {
5263
readonly inline: _angular_core.InputSignalWithTransform<boolean, unknown>;

api-goldens/element-ng/formly-legacy/index.api.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import { AbstractControl } from '@angular/forms';
88
import { AfterContentChecked } from '@angular/core';
99
import { AfterContentInit } from '@angular/core';
10+
import * as _angular_forms_signals from '@angular/forms/signals';
1011
import { ConfigOption } from '@ngx-formly/core';
1112
import { DoCheck } from '@angular/core';
1213
import { ElementRef } from '@angular/core';
@@ -29,6 +30,7 @@ import { OnInit } from '@angular/core';
2930
import { Provider } from '@angular/core';
3031
import { RequiredValidator } from '@angular/forms';
3132
import { Signal } from '@angular/core';
33+
import { SignalFormsConfig } from '@angular/forms/signals';
3234
import { SimpleChanges } from '@angular/core';
3335
import { TranslatableString } from '@siemens/element-translate-ng/translate';
3436

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Copyright (c) Siemens 2016 - 2026
3+
* SPDX-License-Identifier: MIT
4+
*/
5+
import { expect, test } from '../../support/test-helpers';
6+
7+
test.describe('si-signal-form', () => {
8+
const example = 'si-signal-form/si-signal-form';
9+
10+
test(example, async ({ page, si }) => {
11+
await si.visitExample(example, false);
12+
await si.runVisualAndA11yTests(undefined);
13+
14+
// fill the name with an invalid value
15+
const name = page.getByLabel('Name', { exact: true });
16+
await name.fill('a');
17+
18+
await page.getByText('Save').click();
19+
20+
await si.runVisualAndA11yTests('validated');
21+
22+
// verify that the angular provided error messages are linked to the controls
23+
await expect(await si.getDescription(name)).toHaveText(
24+
'Minimum 3 characters Name must start with an uppercase letter'
25+
);
26+
await expect(await si.getDescription(page.getByLabel('Day of birth'))).toHaveText(
27+
'Day of birth required'
28+
);
29+
await expect(await si.getDescription(page.getByLabel('Fellow passengers'))).toHaveText(
30+
'Minimum 2'
31+
);
32+
await expect(
33+
await si.getDescription(page.getByLabel('I confirm that I accept all and everything'))
34+
).toHaveText('Accept terms before joining');
35+
36+
// fixing the name clears its errors
37+
await name.fill('Alice');
38+
await expect(await si.getDescription(name)).toHaveText('');
39+
});
40+
});
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
- heading "Signal form" [level=2]
2+
- text: Name*
3+
- textbox "Name*" [invalid]
4+
- text: Minimum 3 characters Name must start with an uppercase letter Description
5+
- textbox "Description"
6+
- text: Day of birth*
7+
- textbox "Day of birth*" [invalid]
8+
- text: Day of birth required Arrival
9+
- textbox "Arrival"
10+
- text: Departure
11+
- textbox "Departure"
12+
- text: Class of service
13+
- combobox "Class of service":
14+
- option "First class" [selected]
15+
- option "Business"
16+
- option "Economy"
17+
- text: Fellow passengers
18+
- spinbutton "Fellow passengers" [invalid]: "0"
19+
- text: Minimum 2
20+
- checkbox "I confirm that I accept all and everything*" [invalid]
21+
- text: I confirm that I accept all and everything* Accept terms before joining
22+
- checkbox "I confirm that I do not care about my privacy"
23+
- text: I confirm that I do not care about my privacy
24+
- button "Cancel"
25+
- button "Save"
26+
- heading "Debugging" [level=2]
27+
- paragraph: "Validation status: INVALID"
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
- heading "Signal form" [level=2]
2+
- text: Name*
3+
- textbox "Name*"
4+
- text: Description
5+
- textbox "Description"
6+
- text: Day of birth*
7+
- textbox "Day of birth*"
8+
- text: Arrival
9+
- textbox "Arrival"
10+
- text: Departure
11+
- textbox "Departure"
12+
- text: Class of service
13+
- combobox "Class of service":
14+
- option "First class" [selected]
15+
- option "Business"
16+
- option "Economy"
17+
- text: Fellow passengers
18+
- spinbutton "Fellow passengers": "0"
19+
- checkbox "I confirm that I accept all and everything*"
20+
- text: I confirm that I accept all and everything*
21+
- checkbox "I confirm that I do not care about my privacy"
22+
- text: I confirm that I do not care about my privacy
23+
- button "Cancel"
24+
- button "Save"
25+
- heading "Debugging" [level=2]
26+
- paragraph: "Validation status: INVALID"

projects/element-ng/form/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* SPDX-License-Identifier: MIT
44
*/
55
export * from './si-form-container/si-form-container.component';
6+
export * from './si-form-field/si-form-field.component';
7+
export * from './si-form-field/si-form-field.provider';
68
export * from './si-form-item/si-form-item.component';
79
export * from './si-form-validation-tooltip/si-form-validation-tooltip.directive';
810
export * from './form-fieldset/si-form-fieldset.component';

0 commit comments

Comments
 (0)