Skip to content

Commit

Permalink
feat(components/forms): add test harnesses for checkbox and checkbox …
Browse files Browse the repository at this point in the history
…group (#2776)
  • Loading branch information
Blackbaud-CoreyArcher authored Sep 26, 2024
1 parent 8cb3892 commit 7af1c8a
Show file tree
Hide file tree
Showing 16 changed files with 244 additions and 119 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div class="sky-padding-even-large">
<form [formGroup]="formGroup" (ngSubmit)="onSubmit()">
<sky-checkbox-group
data-sky-id="checkbox-group"
headingText="Contact method"
headingStyle="5"
helpPopoverContent="We use your contact info to keep you informed on current events. We will not sell your information."
Expand All @@ -19,13 +20,13 @@
/>
}
</sky-checkbox-group>
<div class="sky-margin-stacked-xl">
<sky-checkbox
formControlName="terms"
labelText="I agree to the terms and conditions"
[required]="true"
/>
</div>
<sky-checkbox
data-sky-id="single-checkbox"
formControlName="terms"
labelText="I agree to the terms and conditions"
[required]="true"
[stacked]="true"
/>
<button class="sky-btn sky-btn-primary" type="submit">Submit</button>
</form>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import {
SkyCheckboxGroupHarness,
SkyCheckboxHarness,
} from '@skyux/forms/testing';

import { DemoComponent } from './demo.component';

describe('Basic checkbox group demo', () => {
async function setupCheckboxGroupTest(options: {
dataSkyId: string;
}): Promise<SkyCheckboxGroupHarness> {
const fixture = TestBed.createComponent(DemoComponent);

const loader = TestbedHarnessEnvironment.loader(fixture);

const harness = await loader.getHarness(
SkyCheckboxGroupHarness.with({ dataSkyId: options.dataSkyId }),
);

fixture.detectChanges();
await fixture.whenStable();

return harness;
}

async function setupCheckboxTest(options: {
dataSkyId: string;
}): Promise<SkyCheckboxHarness> {
const fixture = TestBed.createComponent(DemoComponent);

const loader = TestbedHarnessEnvironment.loader(fixture);

const harness = await loader.getHarness(
SkyCheckboxHarness.with({ dataSkyId: options.dataSkyId }),
);

fixture.detectChanges();
await fixture.whenStable();

return harness;
}

beforeEach(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule, DemoComponent],
});
});

it('should have the appropriate heading text/level/style, label text, and hint text', async () => {
const harness = await setupCheckboxGroupTest({
dataSkyId: 'checkbox-group',
});

const checkboxButtons = await harness.getCheckboxes();

await expectAsync(harness.getHeadingText()).toBeResolvedTo(
'Contact method',
);
await expectAsync(harness.getHeadingLevel()).toBeResolvedTo(undefined);
await expectAsync(harness.getHeadingStyle()).toBeResolvedTo(5);
await expectAsync(harness.getHintText()).toBeResolvedTo(
'Please select at least one phone-based method.',
);

await expectAsync(checkboxButtons[0].getLabelText()).toBeResolvedTo(
'Email',
);
await expectAsync(checkboxButtons[0].getHintText()).toBeResolvedTo('');

await expectAsync(checkboxButtons[1].getLabelText()).toBeResolvedTo(
'Phone',
);
await expectAsync(checkboxButtons[1].getHintText()).toBeResolvedTo('');

await expectAsync(checkboxButtons[2].getLabelText()).toBeResolvedTo('Text');
await expectAsync(checkboxButtons[2].getHintText()).toBeResolvedTo('');
});

it('should display an error message when there is a custom validation error', async () => {
const harness = await setupCheckboxGroupTest({
dataSkyId: 'checkbox-group',
});

const checkboxHarness = (await harness.getCheckboxes())[0];

await checkboxHarness.check();

await expectAsync(harness.hasError('emailOnly')).toBeResolvedTo(true);
});

it('should show a help popover with the expected text', async () => {
const harness = await setupCheckboxGroupTest({
dataSkyId: 'checkbox-group',
});

await harness.clickHelpInline();

const helpPopoverContent = await harness.getHelpPopoverContent();
expect(helpPopoverContent).toBe(
`We use your contact info to keep you informed on current events. We will not sell your information.`,
);
});

it('should check and uncheck checkboxes in display errors if they are required', async () => {
const harness = await setupCheckboxTest({ dataSkyId: 'single-checkbox' });

await expectAsync(harness.isStacked()).toBeResolvedTo(true);

await expectAsync(harness.isChecked()).toBeResolvedTo(false);
await harness.check();
await expectAsync(harness.isChecked()).toBeResolvedTo(true);
await harness.uncheck();
await harness.blur();
await expectAsync(harness.hasRequiredError()).toBeResolvedTo(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -247,12 +247,6 @@ <h3>Reactive checkbox</h3>
helpPopoverContent="content"
/>
<sky-checkbox formControlName="text" labelText="Text" />
@if (contactMethod.errors?.['contactMethodRequired']) {
<sky-form-error
errorName="contactMethodRequired"
errorText="A contact method is required."
/>
}
</sky-checkbox-group>
<div class="sky-margin-stacked-xl">
<sky-checkbox
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import {
AbstractControl,
FormControl,
FormGroup,
NgModelGroup,
UntypedFormBuilder,
UntypedFormGroup,
ValidationErrors,
Validators,
} from '@angular/forms';

Expand Down Expand Up @@ -56,21 +54,6 @@ export class CheckboxComponent implements OnInit {
contactMethod: this.contactMethod,
terms: new FormControl(false),
});

this.contactMethod.setValidators(
(control: AbstractControl): ValidationErrors | null => {
const group = control as FormGroup;
const email = group.controls['email'];
const phone = group.controls['phone'];
const text = group.controls['text'];

if (!email.value && !phone.value && !text.value) {
return { contactMethodRequired: true };
} else {
return null;
}
},
);
}

public ngOnInit(): void {
Expand Down
8 changes: 4 additions & 4 deletions libs/components/forms/src/assets/locales/resources_en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
"_description": "Screen reader only label for character count over limit symbol",
"message": "You are over the character limit."
},
"skyux_checkbox_group_required": {
"_description": "Screen reader text after the checkbox group label when the checkbox group is required",
"message": "Required"
},
"skyux_form_error_character_count": {
"_description": "Error message for a field with a value that exceeds the character count limit",
"message": "Limit {0} to {1} character(s)."
Expand Down Expand Up @@ -75,6 +71,10 @@
"_description": "Error message for a field with an invalid URL value",
"message": "Enter a URL with a valid format."
},
"skyux_form_group_required": {
"_description": "Screen reader text after a form group label when the form group is required",
"message": "Required"
},
"skyux_file_attachment_button_label_choose_file": {
"_description": "Button label for single file attachment when input has no value",
"message": "Attach file"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,26 @@
'sky-control-label-required': required
}"
>
@if (headingLevel === 3) {
<h3 [class]="headingClass">
{{ headingText }}
</h3>
} @else if (headingLevel === 4) {
<h4 [class]="headingClass">
{{ headingText }}
</h4>
} @else if (headingLevel === 5) {
<h5 [class]="headingClass">
{{ headingText }}
</h5>
} @else {
<span [class]="'sky-heading-text ' + headingClass">
{{ headingText }}
</span>
@switch (headingLevel) {
@case (3) {
<h3 [class]="headingClass">{{ headingText }}</h3>
}
@case (4) {
<h4 [class]="headingClass">{{ headingText }}</h4>
}
@case (5) {
<h5 [class]="headingClass">{{ headingText }}</h5>
}
@default {
<span [class]="'sky-checkbox-group-heading-text ' + headingClass">{{
headingText
}}</span>
}
}
</span>
@if (required) {
<span class="sky-screen-reader-only">{{
'skyux_checkbox_group_required' | skyLibResources
'skyux_form_group_required' | skyLibResources
}}</span>
}
@if (helpPopoverContent || helpKey) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
display: block;
}

h3,
h4,
h5 {
margin: 0;
display: inline-block;
.sky-control-label {
h3,
h4,
h5 {
margin: 0;
display: inline-block;
}

span {
line-height: 1.1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const RESOURCES: Record<string, SkyLibResources> = {
skyux_character_count_over_limit: {
message: 'You are over the character limit.',
},
skyux_checkbox_group_required: { message: 'Required' },
skyux_form_error_character_count: {
message: 'Limit {0} to {1} character(s).',
},
Expand Down Expand Up @@ -61,6 +60,7 @@ const RESOURCES: Record<string, SkyLibResources> = {
skyux_form_error_required: { message: '{0} is required.' },
skyux_form_error_time: { message: 'Select or enter a valid time.' },
skyux_form_error_url: { message: 'Enter a URL with a valid format.' },
skyux_form_group_required: { message: 'Required' },
skyux_file_attachment_button_label_choose_file: { message: 'Attach file' },
skyux_file_attachment_button_label_choose_file_label: {
message: 'Attach file for',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { SkyHelpService } from '@skyux/core';
import { SkyHelpTestingModule } from '@skyux/core/testing';

import { SkyCheckboxGroupHarness } from './checkbox-group-harness';
import { CheckboxHarnessTestComponent } from './fixtures/checkbox-harness-test.component';
import { CheckboxHarnessTestModule } from './fixtures/checkbox-harness-test.module';

async function setupTest(options: { dataSkyId?: string } = {}): Promise<{
checkboxGroupHarness: SkyCheckboxGroupHarness;
fixture: ComponentFixture<CheckboxHarnessTestComponent>;
}> {
await TestBed.configureTestingModule({
imports: [CheckboxHarnessTestModule, SkyHelpTestingModule],
imports: [
CheckboxHarnessTestComponent,
SkyHelpTestingModule,
NoopAnimationsModule,
],
}).compileComponents();

const fixture = TestBed.createComponent(CheckboxHarnessTestComponent);
Expand Down
Loading

0 comments on commit 7af1c8a

Please sign in to comment.