Skip to content

Commit

Permalink
Merge pull request #208 from funidata/DS-100-checkbox-group-part-3
Browse files Browse the repository at this point in the history
[CHECKBOX GROUP / VALIDATORS]: fudis group validators with observable messages
  • Loading branch information
RiinaKuu authored Oct 23, 2023
2 parents 6ecb008 + b27721a commit 3911989
Show file tree
Hide file tree
Showing 18 changed files with 186 additions and 112 deletions.
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

0 comments on commit 3911989

Please sign in to comment.