diff --git a/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.html b/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.html
index 2f5f2f99d3..66fe5b1cdb 100644
--- a/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.html
+++ b/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.html
@@ -16,8 +16,17 @@
New member form
-
-
+
+
@@ -69,18 +78,14 @@ New member form
-
+
-
-
-
- {{ colorError.message }}
-
-
+
+
diff --git a/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.spec.ts b/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.spec.ts
index a348799fb8..ba0cf2f438 100644
--- a/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.spec.ts
+++ b/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.spec.ts
@@ -1,6 +1,7 @@
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { SkyAppTestUtility } from '@skyux-sdk/testing';
import { SkyInputBoxHarness } from '@skyux/forms/testing';
import { DemoComponent } from './demo.component';
@@ -40,6 +41,22 @@ describe('Basic input box demo', () => {
});
});
+ describe('last name field', () => {
+ it('should have last name required', async () => {
+ const harness = await setupTest({
+ dataSkyId: 'input-box-last-name',
+ });
+ const inputEl = document.querySelector(
+ 'input.last-name-input-box',
+ ) as HTMLInputElement;
+ inputEl.value = '';
+ SkyAppTestUtility.fireDomEvent(inputEl, 'input');
+ SkyAppTestUtility.fireDomEvent(inputEl, 'blur');
+
+ await expectAsync(harness.hasRequiredError()).toBeResolvedTo(true);
+ });
+ });
+
describe('bio field', () => {
it('should have a character limit of 250', async () => {
const harness = await setupTest({
@@ -71,7 +88,7 @@ describe('Basic input box demo', () => {
});
describe('favorite color field', () => {
- it('should not allow bird to be selected', async () => {
+ it('should not allow invalid color to be selected', async () => {
const harness = await setupTest({
dataSkyId: 'input-box-favorite-color',
});
@@ -80,19 +97,11 @@ describe('Basic input box demo', () => {
'.input-box-favorite-color-select',
) as HTMLSelectElement;
- selectEl.value = 'bird';
+ selectEl.value = 'invalid';
selectEl.dispatchEvent(new Event('change'));
- const customErrors = await harness.getCustomErrors();
-
- expect(customErrors.length).toBe(1);
-
- const birdError = customErrors[0];
-
- await expectAsync(birdError.getDescriptionType()).toBeResolvedTo('error');
- await expectAsync(birdError.getIndicatorType()).toBeResolvedTo('danger');
- await expectAsync(birdError.getText()).toBeResolvedTo(
- 'Bird is not a color.',
+ await expectAsync(harness.hasCustomFormError('invalid')).toBeResolvedTo(
+ true,
);
});
});
diff --git a/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.ts b/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.ts
index c6f2f3abc0..3529ee4acc 100644
--- a/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.ts
+++ b/apps/code-examples/src/app/code-examples/forms/input-box/basic/demo.component.ts
@@ -44,13 +44,8 @@ export class DemoComponent {
constructor() {
this.favoriteColor = new FormControl('none', [
(control): ValidationErrors | null => {
- if (control.value === 'bird') {
- return {
- color: {
- invalid: true,
- message: 'Bird is not a color.',
- },
- };
+ if (control.value === 'invalid') {
+ return { invalid: true };
}
return null;
diff --git a/apps/playground/src/app/components/forms/input-box/input-box.component.html b/apps/playground/src/app/components/forms/input-box/input-box.component.html
index 7e113fea6c..413f6eb57d 100644
--- a/apps/playground/src/app/components/forms/input-box/input-box.component.html
+++ b/apps/playground/src/app/components/forms/input-box/input-box.component.html
@@ -245,6 +245,18 @@
+
+
Help content from template
diff --git a/libs/components/forms/testing/src/input-box/fixtures/input-box-harness-test.component.ts b/libs/components/forms/testing/src/input-box/fixtures/input-box-harness-test.component.ts
index baa02a6c51..3f9b9beaa7 100644
--- a/libs/components/forms/testing/src/input-box/fixtures/input-box-harness-test.component.ts
+++ b/libs/components/forms/testing/src/input-box/fixtures/input-box-harness-test.component.ts
@@ -11,6 +11,7 @@ import {
})
export class InputBoxHarnessTestComponent {
public myForm: UntypedFormGroup;
+ public directiveErrorForm: UntypedFormGroup;
@ViewChild('helpContentTemplate', {
read: TemplateRef,
@@ -32,5 +33,10 @@ export class InputBoxHarnessTestComponent {
firstName: new UntypedFormControl('John'),
lastName: new UntypedFormControl('Doe'),
});
+ this.directiveErrorForm = formBuilder.group({
+ easyModeDatepicker: new UntypedFormControl('123'),
+ easyModeTimepicker: new UntypedFormControl('abc'),
+ easyModePhoneField: new UntypedFormControl('abc'),
+ });
}
}
diff --git a/libs/components/forms/testing/src/input-box/fixtures/input-box-harness-test.module.ts b/libs/components/forms/testing/src/input-box/fixtures/input-box-harness-test.module.ts
index 26b4933f3d..08ca311bf2 100644
--- a/libs/components/forms/testing/src/input-box/fixtures/input-box-harness-test.module.ts
+++ b/libs/components/forms/testing/src/input-box/fixtures/input-box-harness-test.module.ts
@@ -1,8 +1,10 @@
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { SkyIdModule } from '@skyux/core';
+import { SkyDatepickerModule, SkyTimepickerModule } from '@skyux/datetime';
import { SkyInputBoxModule } from '@skyux/forms';
import { SkyStatusIndicatorModule } from '@skyux/indicators';
+import { SkyPhoneFieldModule } from '@skyux/phone-field';
import { InputBoxHarnessTestComponent } from './input-box-harness-test.component';
@@ -13,6 +15,9 @@ import { InputBoxHarnessTestComponent } from './input-box-harness-test.component
SkyIdModule,
SkyInputBoxModule,
SkyStatusIndicatorModule,
+ SkyDatepickerModule,
+ SkyTimepickerModule,
+ SkyPhoneFieldModule,
],
declarations: [InputBoxHarnessTestComponent],
})
diff --git a/libs/components/forms/testing/src/input-box/input-box-harness.spec.ts b/libs/components/forms/testing/src/input-box/input-box-harness.spec.ts
index b349563143..8539749f63 100644
--- a/libs/components/forms/testing/src/input-box/input-box-harness.spec.ts
+++ b/libs/components/forms/testing/src/input-box/input-box-harness.spec.ts
@@ -2,6 +2,7 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Validators } from '@angular/forms';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
+import { SkyValidators } from '@skyux/validation';
import { InputBoxHarnessTestComponent } from './fixtures/input-box-harness-test.component';
import { InputBoxHarnessTestModule } from './fixtures/input-box-harness-test.module';
@@ -138,6 +139,127 @@ describe('Input box harness', () => {
await expectAsync(customError.getText()).toBeResolvedTo('Test error');
});
+ it('should return whether custom form error has fired', async () => {
+ const { inputBoxHarness } = await setupTest({
+ dataSkyId: 'custom-error-easy-mode',
+ });
+
+ await expectAsync(
+ inputBoxHarness.hasCustomFormError('custom'),
+ ).toBeResolvedTo(true);
+ });
+
+ it('should return whether required error has fired', async () => {
+ const { component, fixture, inputBoxHarness } = await setupTest({
+ dataSkyId: 'my-input-box-last-name-easy-mode',
+ });
+
+ const control = component.myForm.controls['lastName'];
+ control.addValidators(Validators.required);
+ control.setValue('');
+ control.markAsDirty();
+
+ fixture.detectChanges();
+
+ await expectAsync(inputBoxHarness.hasRequiredError()).toBeResolvedTo(true);
+ });
+
+ it('should return whether minimum length error has fired', async () => {
+ const { component, fixture, inputBoxHarness } = await setupTest({
+ dataSkyId: 'my-input-box-last-name-easy-mode',
+ });
+
+ const control = component.myForm.controls['lastName'];
+ control.addValidators(Validators.minLength(2));
+ control.setValue('a');
+ control.markAsDirty();
+
+ fixture.detectChanges();
+
+ await expectAsync(inputBoxHarness.hasMinLengthError()).toBeResolvedTo(true);
+ });
+
+ it('should return whether maximum length error has fired', async () => {
+ const { component, fixture, inputBoxHarness } = await setupTest({
+ dataSkyId: 'my-input-box-last-name-easy-mode',
+ });
+
+ const control = component.myForm.controls['lastName'];
+ control.addValidators(Validators.maxLength(1));
+ control.setValue('abc');
+ control.markAsDirty();
+
+ fixture.detectChanges();
+
+ await expectAsync(inputBoxHarness.hasMaxLengthError()).toBeResolvedTo(true);
+ });
+
+ it('should return whether email validator error has fired', async () => {
+ const { component, fixture, inputBoxHarness } = await setupTest({
+ dataSkyId: 'my-input-box-last-name-easy-mode',
+ });
+
+ const control = component.myForm.controls['lastName'];
+ control.addValidators(SkyValidators.email);
+ control.setValue('abc');
+ control.markAsDirty();
+
+ fixture.detectChanges();
+
+ await expectAsync(inputBoxHarness.hasEmailError()).toBeResolvedTo(true);
+ });
+
+ it('should return whether url validator error has fired', async () => {
+ const { component, fixture, inputBoxHarness } = await setupTest({
+ dataSkyId: 'my-input-box-last-name-easy-mode',
+ });
+
+ const control = component.myForm.controls['lastName'];
+ control.addValidators(SkyValidators.url);
+ control.setValue('abc');
+ control.markAsDirty();
+
+ fixture.detectChanges();
+
+ await expectAsync(inputBoxHarness.hasUrlError()).toBeResolvedTo(true);
+ });
+
+ it('should return whether date picker validator error has fired', async () => {
+ const { fixture, inputBoxHarness } = await setupTest({
+ dataSkyId: 'datepicker-easy-mode',
+ });
+
+ fixture.detectChanges();
+
+ await expectAsync(inputBoxHarness.hasDateError()).toBeResolvedTo(true);
+ });
+
+ it('should return whether time picker validator error has fired', async () => {
+ const { component, fixture, inputBoxHarness } = await setupTest({
+ dataSkyId: 'timepicker-easy-mode',
+ });
+
+ const control = component.directiveErrorForm.controls['easyModeTimepicker'];
+ control.markAsDirty();
+ fixture.detectChanges();
+
+ await expectAsync(inputBoxHarness.hasTimeError()).toBeResolvedTo(true);
+ });
+
+ it('should return whether phone field validator error has fired', async () => {
+ const { component, fixture, inputBoxHarness } = await setupTest({
+ dataSkyId: 'phone-field-easy-mode',
+ });
+
+ const control = component.directiveErrorForm.controls['easyModePhoneField'];
+ control.markAsDirty();
+ fixture.detectChanges();
+
+ await expectAsync(inputBoxHarness.hasPhoneFieldError()).toBeResolvedTo(
+ true,
+ );
+ });
+
it('should return character counter indicator', async () => {
const { component, fixture, inputBoxHarness } = await setupTest({
dataSkyId: DATA_SKY_ID_EASY_MODE,
diff --git a/libs/components/forms/testing/src/input-box/input-box-harness.ts b/libs/components/forms/testing/src/input-box/input-box-harness.ts
index 01aefb6fcc..9288756e5f 100644
--- a/libs/components/forms/testing/src/input-box/input-box-harness.ts
+++ b/libs/components/forms/testing/src/input-box/input-box-harness.ts
@@ -9,6 +9,7 @@ import { SkyStatusIndicatorHarness } from '@skyux/indicators/testing';
import { SkyPopoverHarness } from '@skyux/popovers/testing';
import { SkyCharacterCounterIndicatorHarness } from '../character-counter/character-counter-indicator-harness';
+import { SkyFormErrorsHarness } from '../form-error/form-errors-harness';
import { SkyInputBoxHarnessFilters } from './input-box-harness-filters';
@@ -25,6 +26,10 @@ export class SkyInputBoxHarness extends SkyComponentHarness {
#getLabel = this.locatorForOptional('.sky-control-label');
#getWrapper = this.locatorFor('.sky-input-box');
+ async #getFormError(): Promise {
+ return this.locatorFor(SkyFormErrorsHarness)();
+ }
+
/**
* Gets a `HarnessPredicate` that can be used to search for a
* `SkyInputBoxHarness` that meets certain criteria.
@@ -79,6 +84,69 @@ export class SkyInputBoxHarness extends SkyComponentHarness {
return errors;
}
+ /**
+ * Whether the custom error is triggered.
+ */
+ public async hasCustomFormError(errorName: string): Promise {
+ return (await this.#getFormError()).hasError(errorName);
+ }
+
+ /**
+ * Whether the required field is empty.
+ */
+ public async hasRequiredError(): Promise {
+ return (await this.#getFormError()).hasError('required');
+ }
+
+ /**
+ * Whether the field has more characters than allowed.
+ */
+ public async hasMaxLengthError(): Promise {
+ return (await this.#getFormError()).hasError('maxlength');
+ }
+
+ /**
+ * Whether the field has fewer characters than allowed.
+ */
+ public async hasMinLengthError(): Promise {
+ return (await this.#getFormError()).hasError('minlength');
+ }
+
+ /*
+ * Whether the field is set to an invalid email address.
+ */
+ public async hasEmailError(): Promise {
+ return (await this.#getFormError()).hasError('email');
+ }
+
+ /*
+ * Whether the field is set to an invalid URL.
+ */
+ public async hasUrlError(): Promise {
+ return (await this.#getFormError()).hasError('url');
+ }
+
+ /*
+ * Whether the field is set to an invalid date.
+ */
+ public async hasDateError(): Promise {
+ return (await this.#getFormError()).hasError('date');
+ }
+
+ /*
+ * Whether the field is set to an invalid phone number.
+ */
+ public async hasPhoneFieldError(): Promise {
+ return (await this.#getFormError()).hasError('phone');
+ }
+
+ /*
+ * Whether the field is set to an invalid time.
+ */
+ public async hasTimeError(): Promise {
+ return (await this.#getFormError()).hasError('time');
+ }
+
/**
* Indicates whether the input box has disabled styles applied.
*/