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

feat(components/datetime)!: remove deprecated required inputs and update error validation #2654

Merged
merged 23 commits into from
Aug 28, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
stacked="true"
[calculatorIds]="calculatorIds"
[dateFormat]="dateFormat"
[endDateRequired]="endDateRequired"
helpKey="helpKey.html"
[hintText]="hintText"
[startDateRequired]="startDateRequired"
/>

<button class="sky-btn sky-btn-default" type="submit">Submit</button>
Expand Down Expand Up @@ -37,12 +35,6 @@ <h3>Statuses</h3>
<button type="button" (click)="toggleDisabled()">Toggle disabled</button>
<button type="button" (click)="toggleHintText()">Toggle hint text</button>
<button type="button" (click)="toggleRequired()">Toggle required</button>
<button type="button" (click)="toggleStartDateRequired()">
Toggle start date required
</button>
<button type="button" (click)="toggleEndDateRequired()">
Toggle end date required
</button>

<button type="button" (click)="changeDateFormat()">Change date format</button>
<button type="button" (click)="changeLocale()">Change locale</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@ import { LocaleProvider } from './locale-provider';
export class DateRangePickerComponent {
protected calculatorIds: SkyDateRangeCalculatorId[] | undefined;
protected dateFormat: string | undefined;
protected endDateRequired = false;
protected hintText: string | undefined;
protected startDateRequired = false;

protected formGroup = inject(FormBuilder).group({
lastDonation: new FormControl<SkyDateRangeCalculation>(
Expand Down Expand Up @@ -105,7 +103,7 @@ export class DateRangePickerComponent {
startDate: new Date('1/2/2012'),
endDate: new Date('1/1/2012'),
});
this.pickerFormControl.markAsDirty();
this.pickerFormControl.markAsTouched();
}

protected setUndefined(): void {
Expand All @@ -120,10 +118,6 @@ export class DateRangePickerComponent {
}
}

protected toggleEndDateRequired(): void {
this.endDateRequired = !this.endDateRequired;
}

protected toggleHintText(): void {
if (this.hintText) {
this.hintText = undefined;
Expand All @@ -141,8 +135,4 @@ export class DateRangePickerComponent {
}
this.pickerFormControl.updateValueAndValidity();
}

protected toggleStartDateRequired(): void {
this.startDateRequired = !this.startDateRequired;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
<div
class="sky-date-range-picker"
[formGroup]="formGroup"
(focusout)="onBlur()"
>
<div class="sky-date-range-picker" [formGroup]="formGroup">
<div
class="sky-date-range-picker-form-group"
[ngClass]="{
Expand All @@ -11,7 +7,7 @@
}"
>
<sky-input-box
[hasErrors]="hasErrors"
[hasErrors]="calculatorIdHasErrors"
[helpKey]="helpKey"
[helpPopoverContent]="helpPopoverContent"
[helpPopoverTitle]="helpPopoverTitle"
Expand All @@ -22,11 +18,7 @@
('skyux_date_range_picker_default_label' | skyLibResources)
"
>
<select
formControlName="calculatorId"
[required]="isRequired()"
(blur)="onBlur()"
>
<select formControlName="calculatorId" (blur)="onBlur()">
@for (calculator of calculators; track calculator.calculatorId) {
<option [value]="calculator.calculatorId">
{{
Expand All @@ -38,8 +30,9 @@
}
</select>
@if (
hostControl?.errors?.['skyDateRange']?.errors.endDateBeforeStartDate &&
labelText
hostControl?.errors?.['skyDateRange']?.errors?.[
'endDateBeforeStartDate'
] && labelText
) {
<sky-form-error
[errorName]="'endDateBeforeStartDate'"
Expand All @@ -61,8 +54,7 @@
}"
>
<sky-input-box
[errorsScreenReaderOnly]="true"
[hasErrors]="hasErrors"
Blackbaud-SteveBrush marked this conversation as resolved.
Show resolved Hide resolved
[hasErrors]="startDateHasErrors"
[labelText]="
selectedCalculator.type
| skyDateRangePickerStartDateResourceKey
Expand All @@ -88,9 +80,7 @@
| skyLibResources)
"
[dateFormat]="dateFormat"
[required]="
showStartDatePicker && (startDateRequired || isRequired())
"
[required]="showStartDatePicker && isRequired()"
/>
</sky-datepicker>
</sky-input-box>
Expand All @@ -101,8 +91,7 @@
[ngClass]="{ 'sky-date-range-picker-last-input': showEndDatePicker }"
>
<sky-input-box
[errorsScreenReaderOnly]="true"
[hasErrors]="hasErrors"
[hasErrors]="endDateHasErrors"
[labelText]="
selectedCalculator.type
| skyDateRangePickerEndDateResourceKey
Expand All @@ -128,7 +117,7 @@
| skyLibResources)
"
[dateFormat]="dateFormat"
[required]="showEndDatePicker && (endDateRequired || isRequired())"
[required]="showEndDatePicker && isRequired()"
/>
</sky-datepicker>
</sky-input-box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,48 @@ describe('Date range picker', function () {
).toHaveCssClass('ng-touched');
});

it('should mark the underlying control touched if the host control calls markAllAsTouched', () => {
fixture.detectChanges();

component.reactiveForm?.markAllAsTouched();

fixture.detectChanges();

expect(
fixture.nativeElement.querySelector('sky-date-range-picker'),
).toHaveCssClass('ng-touched');
});

it('should mark only start date input as touched when start date is interacted with', () => {
fixture.detectChanges();

const datepickerInputs = fixture.nativeElement.querySelectorAll(
'.sky-input-group input',
);

SkyAppTestUtility.fireDomEvent(datepickerInputs.item(0), 'blur');

fixture.detectChanges();

expect(datepickerInputs.item(0)).toHaveCssClass('ng-touched');
expect(datepickerInputs.item(1)).toHaveCssClass('ng-untouched');
});

it('should mark only start date input as touched when start date is interacted with', () => {
fixture.detectChanges();

const datepickerInputs = fixture.nativeElement.querySelectorAll(
'.sky-input-group input',
);

SkyAppTestUtility.fireDomEvent(datepickerInputs.item(1), 'blur');

fixture.detectChanges();

expect(datepickerInputs.item(1)).toHaveCssClass('ng-touched');
expect(datepickerInputs.item(0)).toHaveCssClass('ng-untouched');
});

it('should maintain selected value when calculators change', fakeAsync(function () {
fixture.detectChanges();

Expand Down Expand Up @@ -633,16 +675,42 @@ describe('Date range picker', function () {
expect(calculatorIdControl?.errors).toBeFalsy();
}));

it('should visually set start and end date datepickers to required and not the calculator select', () => {
fixture.componentInstance.required = true;
fixture.detectChanges();

const control = component.dateRange;
control?.setValue({
calculatorId: SkyDateRangeCalculatorId.SpecificRange,
});
fixture.detectChanges();

const datepickerInputLabels =
fixture.nativeElement.querySelectorAll('.sky-control-label');
const calculatorSelectLabel = datepickerInputLabels.item(0);
const startDateLabel = datepickerInputLabels.item(1);
const endDateLabel = datepickerInputLabels.item(2);

expect(calculatorSelectLabel).not.toHaveCssClass(
'sky-control-label-required',
);
expect(startDateLabel).toHaveCssClass('sky-control-label-required');
expect(endDateLabel).toHaveCssClass('sky-control-label-required');
});

it('should show validation errors when start date is required but not provided', fakeAsync(function () {
fixture.componentInstance.startDateRequired = true;
fixture.componentInstance.required = true;
detectChanges();

const control = component.dateRange;
const calculatorIdControl =
component.dateRangePicker['formGroup']?.get('calculatorId');
const startDateControl =
component.dateRangePicker['formGroup']?.get('startDate');

control?.setValue({
calculatorId: SkyDateRangeCalculatorId.SpecificRange,
});
detectChanges();

const datepickerInputs = fixture.nativeElement.querySelectorAll(
'.sky-input-group input',
);
Expand All @@ -654,31 +722,34 @@ describe('Date range picker', function () {
};

expect(control?.errors).toEqual(expectedError);
expect(calculatorIdControl?.errors).toEqual(expectedError);
expect(startDateControl?.errors).toEqual(expectedError);
}));

it('should show validation errors when end date is required but not provided', fakeAsync(function () {
fixture.componentInstance.endDateRequired = true;
fixture.componentInstance.required = true;
detectChanges();

const control = component.dateRange;
const calculatorIdControl =
component.dateRangePicker['formGroup']?.get('calculatorId');
const endDateControl =
component.dateRangePicker['formGroup']?.get('endDate');

control?.setValue({
calculatorId: SkyDateRangeCalculatorId.SpecificRange,
});
detectChanges();

const datepickerInputs = fixture.nativeElement.querySelectorAll(
'.sky-input-group input',
);

SkyAppTestUtility.fireDomEvent(datepickerInputs.item(1), 'blur');
detectChanges();

const expectedError = {
required: true,
};

expect(control?.errors).toEqual(expectedError);
expect(calculatorIdControl?.errors).toEqual(expectedError);
expect(endDateControl?.errors).toEqual(expectedError);
}));

it('should log a deprecation warning when label input is used', fakeAsync(() => {
Expand All @@ -695,19 +766,25 @@ describe('Date range picker', function () {
}));

it('should render date range specific errors only when labelText is provided', fakeAsync(() => {
component.dateRange?.setValue({
const control = component.dateRange;

control?.setValue({
calculatorId: SkyDateRangeCalculatorId.SpecificRange,
startDate: new Date('1/2/2000'),
endDate: new Date('1/1/2000'),
});
component.reactiveForm.markAllAsTouched();
detectChanges();

control?.updateValueAndValidity();
control?.markAllAsTouched();
detectChanges();

expect(
fixture.nativeElement.querySelector('sky-form-error')?.textContent.trim(),
).toBe(undefined);

component.labelText = 'Date range picker';
control?.updateValueAndValidity();
detectChanges();

expect(
Expand All @@ -730,20 +807,15 @@ describe('Date range picker', function () {
function verifyFormFieldsRequired(expectation: boolean): void {
const inputBoxes =
fixture.nativeElement.querySelectorAll('sky-input-box');
const selectElement = fixture.nativeElement.querySelector('select');
const inputs = fixture.nativeElement.querySelectorAll('input');

expect(
inputBoxes.item(0).querySelector('.sky-control-label-required'),
).toEqual(expectation ? jasmine.any(HTMLLabelElement) : null);
expect(
inputBoxes.item(1).querySelector('.sky-control-label-required'),
).toEqual(expectation ? jasmine.any(HTMLLabelElement) : null);
expect(
inputBoxes.item(2).querySelector('.sky-control-label-required'),
).toEqual(expectation ? jasmine.any(HTMLLabelElement) : null);

expect(selectElement.hasAttribute('required')).toEqual(expectation);
expect(inputs.item(0).hasAttribute('required')).toEqual(expectation);
expect(inputs.item(1).hasAttribute('required')).toEqual(expectation);
}
Expand Down
Loading
Loading