Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CHECKBOX GROUP / VALIDATORS]: fudis group validators with observable messages #208

Merged
merged 3 commits into from
Oct 23, 2023
Merged
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
4 changes: 4 additions & 0 deletions ngx-fudis/projects/dev/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
FudisGridService,
FudisTranslationService,
FudisBreakpointService,
FudisErrorSummaryService,
} from 'ngx-fudis';
import { DOCUMENT } from '@angular/common';

Expand All @@ -30,6 +31,7 @@ export class AppComponent implements OnInit {
private _gridService: FudisGridService,
private _fudisLanguage: FudisTranslationService,
private _alertService: FudisAlertService,
private _errorSummaryService: FudisErrorSummaryService,

private _breakpointService: FudisBreakpointService
) {
Expand Down Expand Up @@ -142,6 +144,8 @@ export class AppComponent implements OnInit {
this._translocoService.setActiveLang('en');
this._fudisLanguage.setLanguage('en');
}

this._errorSummaryService.reloadErrors();
}

openDialog(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@
[title]="t('chooseBerry')"
[helpText]="t('chooseBerryHelp')"
[required]="true"
[groupErrorMsg]="{
atLeastOneRequired: t('chooseBerryError'),
lessThanRequiredRange: t('chooseBerryErrorMin'),
moreThanRequiredRange: t('chooseBerryErrorMax')
}"
[tooltip]="t('chooseBerryTooltip')"
[formGroup]="testFormGroup.controls['checkboxFormGroup']">
<fudis-checkbox
Expand Down Expand Up @@ -65,8 +60,11 @@
[label]="'Basic required text-area'"
[control]="testFormGroup.controls['textArea']"
[maxLength]="20"
[errorMsg]="{ required: t('error_required') }">
</fudis-text-area>
[errorMsg]="{ required: t('error_required') }" />
<fudis-input-with-language-options
[label]="'At least one required'"
[formGroup]="testFormGroup.controls['withLanguages']"
[options]="_languageOptions" />
</ng-template>
</fudis-fieldset>
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { untilDestroyed } from 'projects/ngx-fudis/src/lib/utilities/untilDestroyed';
import { FudisErrorSummaryService } from 'ngx-fudis';
import { FudisFormGroupValidators } from 'projects/ngx-fudis/src/lib/utilities/form/validators';
import { FudisDropdownLanguageOption, FudisInputWithLanguageOptionsFormGroup } from 'dist/ngx-fudis/lib/types/forms';

type MyForm = {
dropdown: FormControl<FudisDropdownOption | null>;
Expand All @@ -22,6 +23,7 @@ type MyForm = {
date: FormControl<Date | null>;
autocompleteDropdown: FormControl<FudisDropdownOption | null>;
autocompleteSearch: FormControl<FudisDropdownOption | null>;
withLanguages: FormGroup<FudisInputWithLanguageOptionsFormGroup>;
};

@Component({
Expand Down Expand Up @@ -72,12 +74,36 @@ export class AppFormExampleComponent implements OnInit {
raspberry: new FormControl<FudisCheckboxOption | null>(null),
strawberry: new FormControl<FudisCheckboxOption | null>(null),
},
[FudisFormGroupValidators.atLeastOneRequired(), FudisFormGroupValidators.outOfRequiredRange(2, 3)]
[
FudisFormGroupValidators.atLeastOneRequired(this._translocoService.selectTranslate('chooseBerryError')),
FudisFormGroupValidators.min({
value: 2,
message: this._translocoService.selectTranslate('chooseBerryErrorMin'),
}),
FudisFormGroupValidators.max({
value: 3,
message: this._translocoService.selectTranslate('chooseBerryErrorMax'),
}),
]
),
autocompleteDropdown: new FormControl<FudisDropdownOption | null>(null, Validators.required),
autocompleteSearch: new FormControl<FudisDropdownOption | null>(null),
withLanguages: new FormGroup<FudisInputWithLanguageOptionsFormGroup>(
{
finnish: new FormControl<string | null>(null),
swedish: new FormControl<string | null>(null),
english: new FormControl<string | null>(null),
},
[FudisFormGroupValidators.atLeastOneRequired(this._translocoService.selectTranslate('error_one_required'))]
),
});

_languageOptions: FudisDropdownLanguageOption[] = [
{ value: 'finnish', viewValue: 'FI' },
{ value: 'swedish', viewValue: 'SV' },
{ value: 'english', viewValue: 'EN' },
];

radioButtonOptions: FudisRadioButtonOption[] = [];

checkboxOptions: FudisCheckboxOption[] = [];
Expand Down
1 change: 1 addition & 0 deletions ngx-fudis/projects/dev/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"helpText": "This is English.",
"required": "Required",
"error_required": "This is required text input",
"error_one_required": "At least one language is required",
"dynamic": "transloco {{value}}",
"chooseBerry": "Choose your berries",
"chooseBerryHelp": "Pick desired amount of berries",
Expand Down
1 change: 1 addition & 0 deletions ngx-fudis/projects/dev/src/assets/i18n/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"helpText": "Kivaa jos näet minut suomeksi!",
"required": "Pakollinen",
"error_required": "Tämä on pakollinen kenttä!",
"error_one_required": "Ainakin yhdellä kielellä tarvitaan tekstiä!",
"dynamic": "transloco {{value}}",
"chooseBerry": "Valitse marjasi",
"chooseBerryHelp": "Valitse toivottu määrä marjoja",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
[inputLabel]="title"
[helpText]="helpText"
[for]="id"
[groupBlurredOut]="_groupBlurredOut"
[groupErrorMsg]="groupErrorMsg"
[groupBlurredOut]="groupBlurredOut"
[formGroup]="formGroup">
</fudis-guidance>
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { FormGroup, Validators } from '@angular/forms';
import { FudisCheckboxGroupFormGroup, FudisFormGroupErrors, FudisInputSize } from '../../../types/forms';
import { FudisCheckboxGroupFormGroup, FudisInputSize } from '../../../types/forms';

import { FieldSetBaseDirective } from '../../../directives/form/fieldset-base/fieldset-base.directive';

Expand All @@ -15,11 +15,6 @@ export class CheckboxGroupComponent extends FieldSetBaseDirective implements OnI
*/
@Input({ required: true }) formGroup: FormGroup<FudisCheckboxGroupFormGroup>;

/*
* Object containing error messages for each FormControl and for the FormGroup.
*/
@Input() groupErrorMsg: FudisFormGroupErrors;

/**
* Set fieldset as required. Alternatively provide FormGroup Validators.required to display 'required' text in the legend lable.
*/
Expand All @@ -38,7 +33,7 @@ export class CheckboxGroupComponent extends FieldSetBaseDirective implements OnI
/**
* To determine if focus has been moved out from the whole checkbox group, so possible errors will not show before that.
*/
protected _groupBlurredOut = false;
public groupBlurredOut = false;

public ngOnInit() {
this.id = this.id ?? this._idService.getNewId('checkboxGroup');
Expand All @@ -53,9 +48,9 @@ export class CheckboxGroupComponent extends FieldSetBaseDirective implements OnI
*/
public setGroupBlurredOut(value: boolean): void {
if (value) {
this._groupBlurredOut = true;
this.groupBlurredOut = true;
} else {
this._groupBlurredOut = false;
this.groupBlurredOut = false;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { StoryFn, Meta, moduleMetadata } from '@storybook/angular';
import { FormsModule, ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms';
import { BehaviorSubject } from 'rxjs';
import { CheckboxGroupComponent } from './checkbox-group.component';
import { FudisCheckboxGroupFormGroup } from '../../../types/forms';
import readme from './readme.mdx';
Expand Down Expand Up @@ -43,7 +44,7 @@ const basicFormGroup = new FormGroup<FudisCheckboxGroupFormGroup>(
pineapple: new FormControl<boolean | null | undefined>(null),
orange: new FormControl<boolean | null | undefined>(null),
},
[FudisFormGroupValidators.atLeastOneRequired()]
[FudisFormGroupValidators.atLeastOneRequired(new BehaviorSubject('No fruit picked! :('))]
);

const withDisabledFormGroupOptions = new FormGroup<FudisCheckboxGroupFormGroup>(
Expand All @@ -54,7 +55,7 @@ const withDisabledFormGroupOptions = new FormGroup<FudisCheckboxGroupFormGroup>(
pineapple: new FormControl<boolean | null | undefined | null>(null),
orange: new FormControl<boolean | null | undefined | null>({ value: null, disabled: true }),
},
[FudisFormGroupValidators.atLeastOneRequired()]
[FudisFormGroupValidators.atLeastOneRequired(new BehaviorSubject('Please pick one! :('))]
);
const withMinMaxFormGroupOptions = new FormGroup<FudisCheckboxGroupFormGroup>(
{
Expand All @@ -64,7 +65,16 @@ const withMinMaxFormGroupOptions = new FormGroup<FudisCheckboxGroupFormGroup>(
pineapple: new FormControl<boolean | null | undefined | null>(null),
orange: new FormControl<boolean | null | undefined | null>(null),
},
[FudisFormGroupValidators.outOfRequiredRange(2, 3)]
[
FudisFormGroupValidators.min({
value: 2,
message: new BehaviorSubject('Not enough fruits picked'),
}),
FudisFormGroupValidators.max({
value: 3,
message: new BehaviorSubject('Too many fruits selected!'),
}),
]
);

const ExampleTemplate: StoryFn<CheckboxGroupComponent> = (args: CheckboxGroupComponent) => ({
Expand All @@ -78,10 +88,7 @@ const ExampleTemplate: StoryFn<CheckboxGroupComponent> = (args: CheckboxGroupCom
[required]="true"
[title]="'Choose your preferred fruits'"
[helpText]="'Pick at least one fruit.'"
[tooltip]="'Fruit sugar is great in small doces!'"
[groupErrorMsg]="{
atLeastOneRequired: 'No fruit picked!'
}">
[tooltip]="'Fruit sugar is great in small doces!'">
<fudis-checkbox *ngFor="let option of options" [controlName]="option.controlName" [label]="option.label" />
</fudis-checkbox-group>`,
});
Expand All @@ -99,10 +106,7 @@ const ExampleWithDisabledTemplate: StoryFn<CheckboxGroupComponent> = (args: Chec
[formGroup]="formGroup"
[required]="true"
[title]="'Choose your preferred fruits'"
[helpText]="'Pick at least one fruit.'"
[groupErrorMsg]="{
atLeastOneRequired: 'No fruit picked!'
}">
[helpText]="'Pick at least one fruit.'">
<fudis-checkbox *ngFor="let option of options" [controlName]="option.controlName" [label]="option.label" />
</fudis-checkbox-group>`,
});
Expand All @@ -120,11 +124,7 @@ const ExampleWithMinMaxTemplate: StoryFn<CheckboxGroupComponent> = (args: Checkb
[formGroup]="formGroup"
[required]="true"
[title]="'Choose your preferred fruits'"
[helpText]="'Pick from two to three fruits.'"
[groupErrorMsg]="{
lessThanRequiredRange: 'Not enough fruits picked',
moreThanRequiredRange: 'Too many fruits selected.'
}">
[helpText]="'Pick from two to three fruits.'">
<fudis-checkbox *ngFor="let option of options" [controlName]="option.controlName" [label]="option.label" />
</fudis-checkbox-group>`,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,15 @@
<span
class="fudis-checkbox__content__box"
[class.fudis-checkbox__content__box--focused]="_focused"
[class.fudis-checkbox__content__box--invalid]="
(_checkboxGroup.formGroup.controls[controlName].invalid &&
_checkboxGroup.formGroup.controls[controlName].touched) ||
_checkboxGroup.invalidState
"
[class.fudis-checkbox__content__box--invalid]="_checkboxGroup.formGroup.invalid && _checkboxGroup.groupBlurredOut"
[class.fudis-checkbox__content__box--disabled]="_checkboxGroup.formGroup.controls[controlName].disabled">
<fudis-icon
*ngIf="_checkboxGroup.formGroup.controls[controlName].value"
[icon]="'check-small'"
class="fudis-checkbox__content__box__icon" />
</span>
<span class="fudis-checkbox__content__label">
{{ label }}
{{ label }} {{ _checkboxGroup.formGroup.controls[controlName].invalid }}
</span>
</span>
</label>
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,20 @@ Checkbox Group is a form fieldset element constructing from `fieldset` `legend`

Necessary properties for Checkbox Group are `title` and `formGroup`.

For child Checkbox elements require `controlName` and `label`. Checkbox's `controlName` must match with the corresponding control in the main `formGroup`.
For child Checkbox elements mandatory properties are `controlName` and `label`. Checkbox's `controlName` must match with the corresponding control in the main `formGroup`.

```
// In HTML template

<fudis-checkbox-group [formGroup]="myFormGroup" [title]="'Example checkbox group'">
<fudis-checkbox [controlName]="'firstControlName'" [label]="'First checkbox'"/>
<fudis-checkbox [controlName]="'secondControlName'" [label]="'Second checkbox'"/>
<fudis-checkbox-group
[formGroup]="myFormGroup"
[title]="'Example checkbox group'">
<fudis-checkbox
[controlName]="'firstControlName'"
[label]="'First checkbox'"/>
<fudis-checkbox
[controlName]="'secondControlName'"
[label]="'Second checkbox'"/>
</fudis-checkbox-group>


Expand All @@ -40,7 +46,7 @@ myFormGroup = new FormGroup<FudisCheckboxGroupFormGroup>(
);
```

#### As a required form field
#### As a required form field and at least one required selection

To display visible 'required' text with the fieldset group, set `required` input as true to Checkbox Group and include validator `FudisFormGroupValidators.atLeastOneRequired()` in the validators array of `formGroup`.

Expand All @@ -50,41 +56,48 @@ To display visible 'required' text with the fieldset group, set `required` input
<fudis-checkbox-group
[formGroup]="myFormGroup"
[title]="'Choose at least one'"
[required]="true"
[groupErrorMsg]="{atLeastOneRequired: 'No checkbox checked!'}">
<fudis-checkbox [controlName]="'firstControlName'" [label]="'You should check this'"/>
<fudis-checkbox [controlName]="'secondControlName'" [label]="'Or this, or both!'"/>
[required]="true">
<fudis-checkbox
[controlName]="'first'"
[label]="'You should check this'"/>
<fudis-checkbox
[controlName]="'second'"
[label]="'Or this, or both!'"/>
</fudis-checkbox-group>

// In TypeScript file

myFormGroup = new FormGroup<FudisCheckboxGroupFormGroup>(
{
firstControlName: new FormControl<boolean | null | undefined>(null),
secondControlName: new FormControl<boolean | null | undefined>(null),
},[FudisFormGroupValidators.atLeastOneRequired()]
first: new FormControl<boolean | null | undefined>(null),
second: new FormControl<boolean | null | undefined>(null),
},
[FudisFormGroupValidators.atLeastOneRequired(new BehaviorSubject('Select at least one checkbox!'))]
);


```

#### Minimum and maximum selection validation

If checkbox-group fieldset is a required form field include `Validators.required` in the form control validators and include a `required` error message in the `errorMsg` property.
If checkbox-group fieldset has minimum and/or maximum options to be selected, form field include `FudisFormGroupValidators.min` and/or `FudisFormGroupValidators.max` in the group validators.

#### Minimum and maximum selectable options

If checkbox-group fieldset has minimum and/or maximum options to be selected, form field include `Validators.min` and/or `Validators.max` in the form control validators and include a `min` and/or `max` error message in the `errorMsg` property.

### Error Handling
```
// In TypeScript file

- **Required** error is shown if checkbox-group fieldset is required and
- if control is touched and
- if control value is null
- **Minimum** error is shown if checkbox-group fieldset is required and
- if control is touched and
- if control value is less than the given minimum value
- **Maximum** error is shown if checkbox-group control is touched and
- if control value is more than the given maximum value
myFormGroup = new FormGroup<FudisCheckboxGroupFormGroup>(
{
first: new FormControl<boolean | null | undefined>(null),
second: new FormControl<boolean | null | undefined>(null),
third: new FormControl<boolean | null | undefined>(null),
forth: new FormControl<boolean | null | undefined>(null),
},
[
FudisFormGroupValidators.min({value: 2, message: new BehaviorSubject('Too few selected!')}),
FudisFormGroupValidators.max({value: 3, message: new BehaviorSubject('Too many selected!')})
]
);
```

### Accessibility

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<p class="fudis-error-message fudis-error-message__{{ variant }}" *ngIf="message && visible">
{{ message }}
<p class="fudis-error-message fudis-error-message__{{ variant }}" *ngIf="_currentMessage && visible">
{{ _currentMessage }}
</p>
Loading