Skip to content

Commit

Permalink
fix(components/lookup): lookup aria labels are now set appropriately …
Browse files Browse the repository at this point in the history
…when using the input box `labelText` input
  • Loading branch information
Blackbaud-TrevorBurch committed Jul 24, 2024
1 parent deab22a commit f26705b
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ describe('Input box host service', () => {
],
{
ariaDescribedBy: new ReplaySubject<string>(1),
controlId: 'controlId',
labelId: 'labelId',
labelText: 'labelText',
},
);

Expand Down Expand Up @@ -104,10 +107,39 @@ describe('Input box host service', () => {
expect(hostService.controlId).toBe('');
});

it('should return control ID when host is defined', () => {
hostService.init(mockInputBox);

expect(hostService.controlId).toBe('controlId');
});

it('should return an empty string for label ID when host is undefined', () => {
expect(hostService.labelId).toBe('');
});

it('should return label ID when host is defined', () => {
hostService.init(mockInputBox);

expect(hostService.labelId).toBe('labelId');
});

it('should return label ID when host is defined but label text is undefined', () => {
mockInputBox.labelText = undefined;
hostService.init(mockInputBox);

expect(hostService.labelId).toBe('labelId');
});

it('should return an empty string for label text when host is undefined', () => {
expect(hostService.labelText).toBe('');
});

it('should return label text when host is defined', () => {
hostService.init(mockInputBox);

expect(hostService.labelText).toBe('labelText');
});

it('should undefined for ariaDescribedBy when host is undefined', () => {
expect(hostService.ariaDescribedBy).toBeUndefined();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export class SkyInputBoxHostService {
return this.#host?.controlId ?? '';
}

public get labelId(): string {
return this.#host?.labelText ? this.#host.labelId : '';
}

public get labelText(): string {
return this.#host?.labelText ?? '';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
: null
"
[for]="controlId"
[id]="labelId"
[ngClass]="{
'sky-control-label-required': required
}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export class SkyInputBoxComponent
protected hostHintText: string | undefined;

public readonly controlId = this.#idSvc.generateId();
public readonly labelId = this.#idSvc.generateId();
public readonly errorId = this.#idSvc.generateId();
public readonly hintTextId = this.#idSvc.generateId();
public readonly ariaDescribedBy = new ReplaySubject<string | undefined>(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
<form [formGroup]="form" novalidate>
<sky-input-box hintText="Some hint text." id="my-lookup">
<label id="my-lookup-label"> My friends </label>
<sky-input-box
labelText="My friends"
hintText="Some hint text."
id="my-lookup"
>
<sky-lookup
formControlName="friends"
[ariaLabel]="ariaLabel"
[ariaLabelledBy]="ariaLabelledBy"
[autocompleteAttribute]="autocompleteAttribute"
[data]="data"
[enableShowMore]="enableShowMore"
Expand All @@ -13,3 +18,4 @@
<button class="sky-btn sky-btn-primary" id="test-button" type="button">
Test
</button>
<label id="my-lookup-label">Testing label</label>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ export class SkyLookupInputBoxTestComponent {
})
public lookupComponent!: SkyLookupComponent;

public ariaLabel: string | undefined;

public ariaLabelledBy: string | undefined;

public autocompleteAttribute: string | undefined;

public data: any[] = [];
Expand Down Expand Up @@ -55,4 +59,24 @@ export class SkyLookupInputBoxTestComponent {
friends: new UntypedFormControl(this.friends),
});
}

public resetForm(): void {
this.form.reset();
}

public setMultiSelect(): void {
this.selectMode = 'multiple';
}

public setSingleSelect(): void {
this.selectMode = 'single';
}

public setValue(index: number | undefined): void {
if (this.data && index) {
this.form.controls['friends'].setValue([this.data[index]]);
} else {
this.form.controls['friends'].setValue(undefined);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
#lookupWrapper
>
<sky-autocomplete
[ariaLabelledBy]="ariaLabelledBy || controlId"
[ariaLabelledBy]="ariaLabelledBy || inputBoxHostSvc?.labelId || controlId"
[attr.data-sky-lookup-show-more-picker-id]="showMorePickerId"
[data]="data"
[debounceTime]="debounceTime"
Expand Down
216 changes: 162 additions & 54 deletions libs/components/lookup/src/lib/modules/lookup/lookup.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4026,60 +4026,6 @@ describe('Lookup component', function () {
}));
});

describe('a11y', function () {
const axeConfig = {
rules: {
region: {
enabled: false,
},
},
};

it('should be accessible', async () => {
fixture.componentInstance.ariaLabelledBy = 'my-lookup-label';
fixture.componentInstance.setSingleSelect();
fixture.componentInstance.setValue(2);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);

fixture.componentInstance.ariaLabel = 'My lookup label';
fixture.componentInstance.ariaLabelledBy = undefined;

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);

fixture.componentInstance.setMultiSelect();
fixture.componentInstance.setValue(1);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);

fixture.componentInstance.setSingleSelect();
fixture.componentInstance.resetForm();

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);

const inputElement = getInputElement(
fixture.componentInstance.lookupComponent,
);
inputElement.value = 'r';
inputElement.focus();
SkyAppTestUtility.fireDomEvent(inputElement, 'keyup', {
keyboardEventInit: { key: '' },
});

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});
});

// for testing non-async search args being passed around correctly
describe('search args (non-async)', () => {
// to test the passing of the 'context' arg
Expand Down Expand Up @@ -7573,5 +7519,167 @@ describe('Lookup component', function () {
expect(document.activeElement).not.toEqual(input);
}));
});

describe('a11y', function () {
const axeConfig = {
rules: {
region: {
enabled: false,
},
},
};

beforeEach(() => {
fixture.detectChanges();
});

it('should be accessible [mode: single, value: set, ariaLabel: undefined, ariaLabelledBy: set]', async () => {
fixture.componentInstance.ariaLabelledBy = 'my-lookup-label';
fixture.componentInstance.setSingleSelect();
fixture.componentInstance.setValue(2);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: single, value: set, ariaLabel: set, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setSingleSelect();
fixture.componentInstance.setValue(2);
fixture.componentInstance.ariaLabel = 'My lookup label';

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: single, value: set, ariaLabel: undefined, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setSingleSelect();
fixture.componentInstance.setValue(2);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: single, value: undefined, ariaLabel: undefined, ariaLabelledBy: set]', async () => {
fixture.componentInstance.ariaLabelledBy = 'my-lookup-label';
fixture.componentInstance.setSingleSelect();
fixture.componentInstance.setValue(undefined);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: single, value: undefined, ariaLabel: set, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setSingleSelect();
fixture.componentInstance.setValue(undefined);
fixture.componentInstance.ariaLabel = 'My lookup label';

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: single, value: undefined, ariaLabel: undefined, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setSingleSelect();
fixture.componentInstance.setValue(undefined);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: multiple, value: set, ariaLabel: undefined, ariaLabelledBy: set]', async () => {
fixture.componentInstance.ariaLabelledBy = 'my-lookup-label';
fixture.componentInstance.setMultiSelect();
fixture.componentInstance.setValue(2);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: multiple, value: set, ariaLabel: set, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setMultiSelect();
fixture.componentInstance.setValue(2);
fixture.componentInstance.ariaLabel = 'My lookup label';

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: multiple, value: set, ariaLabel: undefined, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setMultiSelect();
fixture.componentInstance.setValue(2);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: multiple, value: undefined, ariaLabel: undefined, ariaLabelledBy: set]', async () => {
fixture.componentInstance.ariaLabelledBy = 'my-lookup-label';
fixture.componentInstance.setMultiSelect();
fixture.componentInstance.setValue(undefined);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: multiple, value: undefined, ariaLabel: set, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setMultiSelect();
fixture.componentInstance.setValue(undefined);
fixture.componentInstance.ariaLabel = 'My lookup label';

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: multiple, value: undefined, ariaLabel: undefined, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setMultiSelect();
fixture.componentInstance.setValue(undefined);

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: single, value: input typed, ariaLabel: undefined, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setSingleSelect();
const inputElement = getInputElement(
fixture.componentInstance.lookupComponent,
);
inputElement.value = 'r';
inputElement.focus();
SkyAppTestUtility.fireDomEvent(inputElement, 'keyup', {
keyboardEventInit: { key: '' },
});

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});

it('should be accessible [mode: multiple, value: input typed, ariaLabel: undefined, ariaLabelledBy: undefined]', async () => {
fixture.componentInstance.setMultiSelect();
const inputElement = getInputElement(
fixture.componentInstance.lookupComponent,
);
inputElement.value = 'r';
inputElement.focus();
SkyAppTestUtility.fireDomEvent(inputElement, 'keyup', {
keyboardEventInit: { key: '' },
});

fixture.detectChanges();
await fixture.whenStable();
await expectAsync(document.body).toBeAccessible(axeConfig);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export class SkyLookupComponent
* [to support accessibility](https://developer.blackbaud.com/skyux/learn/accessibility).
* If the input includes a visible label, use `ariaLabelledBy` instead.
* For more information about the `aria-label` attribute, see the [WAI-ARIA definition](https://www.w3.org/TR/wai-aria/#aria-label).
* @deprecated Use the input box `labelText` input instead.
*/
@Input()
public ariaLabel: string | undefined;
Expand All @@ -76,6 +77,7 @@ export class SkyLookupComponent
* [to support accessibility](https://developer.blackbaud.com/skyux/learn/accessibility).
* If the input does not include a visible label, use `ariaLabel` instead.
* For more information about the `aria-labelledby` attribute, see the [WAI-ARIA definition](https://www.w3.org/TR/wai-aria/#aria-labelledby).
* @deprecated Use the input box `labelText` input instead.
*/
@Input()
public ariaLabelledBy: string | undefined;
Expand Down

0 comments on commit f26705b

Please sign in to comment.