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

Zhylka/ #2676 entrering workshops hours using keyboard #2678

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
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 @@ -13,6 +13,8 @@
<ng-container *ngIf="invalidStreet; then invalidStreetTmpl"></ng-container>
<ng-container *ngIf="invalidHouse; then invalidHouseTmpl"></ng-container>
<ng-container *ngIf="invalidSearch; then invalidSearchTmpl"></ng-container>
<ng-container *ngIf="invalidTimeFormat; then invalidTimeFormatTmpl"></ng-container>
<ng-container *ngIf="invalidTimeRange; then invalidTimeRangeTmpl"></ng-container>
<ng-container *ngIf="invalidSectionName && !validationFormControl.errors.minlength; then invalidSectionNameTmpl"></ng-container>
<ng-container
*ngIf="
Expand Down Expand Up @@ -84,3 +86,7 @@
</ng-template>

<ng-template #invalidSearchTmpl> {{ 'FORMS.VALIDATIONS.INVALID_SEARCH' | translate }}<br /> </ng-template>

<ng-template #invalidTimeRangeTmpl>{{ 'FORMS.VALIDATIONS.INVALID_TIME_RANGE' | translate }}</ng-template>

<ng-template #invalidTimeFormatTmpl>{{ 'FORMS.VALIDATIONS.INVALID_TIME_FORMAT' | translate }}</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,18 @@ describe('ValidationHintComponent', () => {
component.validationFormControl.setValue('test');
tick(200);
}));
it('should validate TimeFormat in FormLevel', () => {
const control1 = new FormControl('');
const formGroup = new FormGroup({ control1: control1 });
formGroup.setErrors({ invalidTimeRange: true });
component.validationFormControl = formGroup;

component.validationFormControl.statusChanges.pipe(tap(() => tick(200))).subscribe(() => {
expect(component.invalidTimeRange).toBeTruthy();
});

component.ngOnInit();
});
});

describe('checkValidationErrors method', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export class ValidationHintComponent implements OnInit, OnDestroy, OnChanges {
public invalidSectionName: boolean;
public mustContainLetters: boolean;
public invalidSearch: boolean;
public invalidTimeFormat: boolean;
public invalidTimeRange: boolean;

private destroy$: Subject<boolean> = new Subject<boolean>();

Expand All @@ -58,6 +60,7 @@ export class ValidationHintComponent implements OnInit, OnDestroy, OnChanges {
Object.keys(this.validationFormControl.controls).forEach((key) => {
this.updateValidationState(this.validationFormControl.get(key) as FormControl);
});
this.checkFormLevelValidationErrors(this.validationFormControl.errors);
} else {
this.updateValidationState(this.validationFormControl as FormControl);
}
Expand Down Expand Up @@ -114,6 +117,11 @@ export class ValidationHintComponent implements OnInit, OnDestroy, OnChanges {
} else {
this.invalidFieldLength = errors?.maxlength || errors?.minlength;
}
this.invalidTimeFormat = errors?.invalidTimeFormat;
}

private checkFormLevelValidationErrors(errors: ValidationErrors): void {
this.invalidTimeRange = errors?.invalidTimeRange;
}

private checkInvalidText(errors: ValidationErrors): void {
Expand Down
6 changes: 6 additions & 0 deletions src/app/shared/constants/regex-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,9 @@ export const SEARCHBAR_REGEX_VALID: RegExp = /^[A-Za-zА-Яа-яІіЇїЄєҐґ

// Regex for searchbar replace invalid characters
export const SEARCHBAR_REGEX_REPLACE: RegExp = /[^A-Za-zА-Яа-яІіЇїЄєҐґ0-9`.,№"'\s]/g;

// Regex for time format validation
export const TIME_FORMAT_REGEX: RegExp = /^(2[0-3]|[01]?\d):([0-5]\d)$/;

// Regex for time input replace invalid characters
export const TIME_REGEX_REPLACE: RegExp = /[^0-9:]/g;
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@
<app-working-hours-form-wrapper
[workingHoursFormArray]="workingHoursFormArray"
[workshop]="workshop"
(dataChangedInChild)="markFormAsDirtyOnUserInteraction()"></app-working-hours-form-wrapper>
(dataChangedInChild)="markFormAsDirtyOnUserInteraction()">
</app-working-hours-form-wrapper>

<label class="step-label">{{ 'FORMS.LABELS.FORM_OF_LEARNING' | translate }}<span class="step-required">*</span></label>
<mat-radio-group formControlName="formOfLearning" color="primary" class="flex flex-col justify-between items-stretch">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<form class="step">
<div class="flex flex-col">
<div class="flex flex-row">
<div class="flex flex-col gap-2">
<div class="days">
<div
class="flex flex-row justify-between"
Expand All @@ -21,50 +21,61 @@
<p class="text">{{ 'FROM' | translate }}</p>
<input
class="input step-input start-time-input"
type="text"
matInput
[ngxMatTimepicker]="timepickerStart"
[format]="24"
readonly
required
(input)="validateTimeInput($event)"
[formControl]="startTimeFormControl"
[max]="getMaxTime()"
required
(blur)="onStartBlur()"
maxlength="5"
autocomplete="off" />
<button mat-icon-button [disabled]="!startTimeFormControl.enabled" (click)="timepickerStart.open()"> <mat-icon>access_time</mat-icon></button>
</div>
</div>

<div class="flex flex-col justify-start items-start">
<div class="time-input flex flex-row justify-start items-center">
<p class="text">{{ 'TO' | translate }}</p>
<input
class="input step-input finish-time-input"
type="text"
class="input step-input start-time-input"
matInput
[ngxMatTimepicker]="timepickerEnd"
[format]="24"
readonly
required
(input)="validateTimeInput($event)"
[formControl]="endTimeFormControl"
[min]="getMinTime()"
[max]="ValidationConstants.MAX_TIME"
required
(blur)="onEndBlur()"
maxlength="5"
autocomplete="off" />
<button mat-icon-button [disabled]="!endTimeFormControl.enabled" (click)="timepickerEnd.open()"> <mat-icon>access_time</mat-icon></button>
</div>
</div>
<div class="delete-block flex flex-row justify-start items-start" *ngIf="workingHoursAmount > 1">
<mat-icon class="mat-icon-delete" (click)="delete()">delete</mat-icon>
</div>
</div>
</div>
<app-validation-hint [validationFormControl]="startTimeFormControl"></app-validation-hint>
<app-validation-hint *ngIf="isEditMode" [validationFormControl]="endTimeFormControl"></app-validation-hint>
<app-validation-hint [validationFormControl]="workingHoursForm"></app-validation-hint>
</div>
</form>

<ng-template #ValidationHintTmpl>
<small appValidationMessageStyling>{{ 'FORMS.VALIDATIONS.REQUIRED_INPUT' | translate }}<br /></small>
</ng-template>

<ngx-mat-timepicker #timepickerStart [cancelBtnTmpl]="btnCancel" [confirmBtnTmpl]="btnConfirm"></ngx-mat-timepicker>
<ngx-mat-timepicker #timepickerEnd [cancelBtnTmpl]="btnCancel" [confirmBtnTmpl]="btnConfirm"></ngx-mat-timepicker>

<ngx-mat-timepicker
#timepickerStart
[format]="24"
[defaultTime]="startTimeFormControl.value"
(timeSet)="onStartTimeSet($event)"
[cancelBtnTmpl]="btnCancel"
[confirmBtnTmpl]="btnConfirm"
></ngx-mat-timepicker>
<ngx-mat-timepicker
#timepickerEnd
[format]="24"
[defaultTime]="endTimeFormControl.value"
(timeSet)="onEndTimeSet($event)"
[cancelBtnTmpl]="btnCancel"
[confirmBtnTmpl]="btnConfirm">
</ngx-mat-timepicker>
<ng-template #btnCancel>
<button mat-raised-button class="btn btn-cancel" (click)="onCancel()">{{ 'BUTTONS.CANCEL' | translate }}</button>
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,9 @@
gap: 5px;
}
}

button:disabled,
button {
border: none;
background-color: transparent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { NgxMatTimepickerModule } from 'ngx-mat-timepicker';
import { Component, Input } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { MaterialModule } from '../../../../../../../shared/modules/material.module';
import { WorkingHoursFormComponent } from './working-hours-form.component';
import { timeRangeValidator, WorkingHoursFormComponent } from './working-hours-form.component';

describe('WorkingHoursFormComponent', () => {
let component: WorkingHoursFormComponent;
Expand Down Expand Up @@ -49,6 +49,98 @@ describe('WorkingHoursFormComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

it('should return null for valid time range', () => {
const formGroup = new FormGroup(
{
startTime: new FormControl('08:00'),
endTime: new FormControl('18:00')
},
[timeRangeValidator('startTime', 'endTime')]
);

expect(formGroup.errors).toBeNull();
});

it('should return error when start time is after end time', () => {
const formGroup = new FormGroup(
{
startTime: new FormControl('19:00'),
endTime: new FormControl('18:00')
},
[timeRangeValidator('startTime', 'endTime')]
);

expect(formGroup.errors).toEqual({ invalidTimeRange: true });
expect(formGroup.get('startTime')?.errors).toEqual({ invalidTimeRange: true });
expect(formGroup.get('endTime')?.errors).toEqual({ invalidTimeRange: true });
});

it('should be valid startTime and endTime', () => {
const startTime = component.workingHoursForm.get('startTime');
const endTime = component.workingHoursForm.get('endTime');

startTime?.setValue('08:00');
endTime?.setValue('18:00');
expect(component.workingHoursForm.valid).toBeTruthy();

endTime?.setValue('07:00');
expect(component.workingHoursForm.errors).toEqual({ invalidTimeRange: true });
});

it('should be invalid startTime and endTime', () => {
const startTime = component.workingHoursForm.get('startTime');
startTime?.setValue('12:00');
expect(startTime.valid).toBeTruthy();

startTime?.setValue('as:00');

expect(startTime.errors).toEqual({ invalidTimeFormat: true });
});

it('should set startTime to the maximum value if left empty on blur', () => {
const startTime = component.workingHoursForm.get('startTime');
startTime?.setValue('');

component.onStartBlur();

expect(startTime.value).toEqual('23:58');
});

it('should set endTime to the minimum value if left empty on blur', () => {
const startTime = component.workingHoursForm.get('startTime');
const endTime = component.workingHoursForm.get('endTime');
startTime?.setValue('23:50');

component.onEndBlur();

expect(endTime.value).toEqual('23:51');
});

it('should clean input value by removing non-numeric and non-colon characters', () => {
const event = {
target: {
value: '12a:b3#4$'
}
} as unknown as Event;

const inputElement = event.target as HTMLInputElement;

component.validateTimeInput(event);

expect(inputElement.value).toBe('12:34');
});

it('should set time via timePicker', () => {
component.startTimeFormControl.setValue('');
component.endTimeFormControl.setValue('');

component.onStartTimeSet('12:30');
component.onEndTimeSet('14:30');

expect(component.startTimeFormControl.value).toBe('12:30');
expect(component.endTimeFormControl.value).toBe('14:30');
});
});
@Component({
selector: 'app-validation-hint',
Expand Down
Loading
Loading