Skip to content

Commit

Permalink
va-checkbox | Add description id to aria-describedby (#817)
Browse files Browse the repository at this point in the history
* Add description id to aria-describedby

* Remove duplicate assets

* bump versions
  • Loading branch information
Mottie authored Aug 29, 2023
1 parent fc38c81 commit 4c97574
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 21 deletions.
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@department-of-veterans-affairs/component-library",
"description": "VA.gov component library. Includes React and web components.",
"version": "19.1.0",
"version": "19.1.1",
"license": "MIT",
"scripts": {
"build": "webpack"
Expand Down
2 changes: 1 addition & 1 deletion packages/web-components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@department-of-veterans-affairs/web-components",
"version": "4.45.10",
"version": "4.45.11",
"description": "Stencil Component Starter",
"main": "dist/index.cjs.js",
"module": "dist/index.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ describe('va-checkbox', () => {
await page.setContent(
'<va-checkbox error="Something went horribly wrong" />',
);
const element = await page.find('va-checkbox >>> #error-message');
const element = await page.find('va-checkbox >>> #checkbox-error-message');
const input = await page.find('va-checkbox >>> input');
expect(input.getAttribute('aria-invalid')).toEqual('true');
expect(input.getAttribute('aria-describedby')).toEqual('checkbox-error-message');
expect(element.textContent).toContain('Something went horribly wrong');
});

Expand All @@ -58,7 +59,9 @@ describe('va-checkbox', () => {
'<va-checkbox><p slot="description">This is a description!</p></va-checkbox',
);
const element = await page.find('va-checkbox');
const inputEl = await page.find('va-checkbox >>> input');
expect(element).toEqualText('This is a description!');
expect(inputEl.getAttribute('aria-describedby')).toEqual('description');
});

// This test fails, but is here as documentation. The unknown slot and unnamed
Expand All @@ -79,10 +82,12 @@ describe('va-checkbox', () => {
'<va-checkbox description="Description prop"><p slot="description">Slotted description</p></va-checkbox',
);
const element = await page.find('va-checkbox');
const inputEl = await page.find('va-checkbox >>> input');
expect(element.shadowRoot.textContent).toContain('Description prop');
expect(
await page.find('va-checkbox >>> slot[name="description"]'),
).toBeNull();
expect(inputEl.getAttribute('aria-describedby')).toEqual('description');
});

it('adds new aria-describedby for error message', async () => {
Expand All @@ -91,7 +96,7 @@ describe('va-checkbox', () => {

// Render the error message text
const inputEl = await page.find('va-checkbox >>> input');
expect(inputEl.getAttribute('aria-describedby')).toContain('error-message');
expect(inputEl.getAttribute('aria-describedby')).toContain('checkbox-error-message');
});

it('adds aria-describedby input-message id', async () => {
Expand All @@ -103,6 +108,19 @@ describe('va-checkbox', () => {
expect(inputEl.getAttribute('aria-describedby')).toContain('input-message');
});

it('adds aria-describedby input-message, checkbox-error-message and description ids', async () => {
const page = await newE2EPage();
await page.setContent(
`<va-checkbox error="Something went horribly wrong" message-aria-describedby="This is a message">
<p slot="description">Slotted description</p>
</va-checkbox>`,
);

// Render the error message text
const inputEl = await page.find('va-checkbox >>> input');
expect(inputEl.getAttribute('aria-describedby')).toEqual('input-message checkbox-error-message description');
});

it('passes an aXe check', async () => {
const page = await newE2EPage();

Expand Down Expand Up @@ -157,6 +175,7 @@ describe('va-checkbox', () => {
required: false,
},
});
expect(inputEl.getAttribute('aria-describedby')).toEqual('description');
});

it("doesn't fire an analytics event when enableAnalytics is false", async () => {
Expand Down Expand Up @@ -263,6 +282,7 @@ describe('va-checkbox', () => {
const element = await page.find('va-checkbox >>> #checkbox-error-message');
const input = await page.find('va-checkbox >>> input');
expect(input.getAttribute('aria-invalid')).toEqual('true');
expect(input.getAttribute('aria-describedby')).toEqual('checkbox-error-message');
expect(element.textContent).toContain('Something went horribly wrong');
});

Expand All @@ -287,7 +307,9 @@ describe('va-checkbox', () => {
'<va-checkbox uswds><p slot="description">This is a description!</p></va-checkbox',
);
const element = await page.find('va-checkbox');
const inputEl = await page.find('va-checkbox >>> input');
expect(element).toEqualText('This is a description!');
expect(inputEl.getAttribute('aria-describedby')).toEqual('description');
});

it('uswds v3 should prefer rendering the description prop over the slotted element', async () => {
Expand All @@ -296,10 +318,12 @@ describe('va-checkbox', () => {
'<va-checkbox uswds description="Description prop"><p slot="description">Slotted description</p></va-checkbox',
);
const element = await page.find('va-checkbox');
const inputEl = await page.find('va-checkbox >>> input');
expect(element.shadowRoot.textContent).toContain('Description prop');
expect(
await page.find('va-checkbox >>> slot[name="description"]'),
).toBeNull();
expect(inputEl.getAttribute('aria-describedby')).toEqual('description');
});

it('uswds v3 adds new aria-describedby for error message', async () => {
Expand All @@ -320,6 +344,19 @@ describe('va-checkbox', () => {
await axeCheck(page);
});

it('uswds v3 adds aria-describedby input-message, checkbox-error-message and description ids', async () => {
const page = await newE2EPage();
await page.setContent(
`<va-checkbox uswds error="Something went horribly wrong" message-aria-describedby="This is a message">
<p slot="description">Slotted description</p>
</va-checkbox>`,
);

// Render the error message text
const inputEl = await page.find('va-checkbox >>> input');
expect(inputEl.getAttribute('aria-describedby')).toEqual('input-message checkbox-error-message description');
});

it('uswds v3 fires an analytics event when enableAnalytics is true', async () => {
const page = await newE2EPage();
await page.setContent(
Expand Down
45 changes: 28 additions & 17 deletions packages/web-components/src/components/va-checkbox/va-checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,20 +165,23 @@ export class VaCheckbox {
}

render() {
const {
error,
label,
required,
description,
checked,
hint,
const {
error,
label,
required,
description,
checked,
hint,
tile,
uswds,
checkboxDescription,
disabled,
uswds,
checkboxDescription,
disabled,
messageAriaDescribedby,
} = this;


const hasDescription = description || !!this.el.querySelector('[slot="description"]');

if (uswds) {
const inputClass = classnames({
'usa-checkbox__input': true,
Expand All @@ -188,11 +191,15 @@ export class VaCheckbox {
'usa-legend': true,
'usa-label--error': error
});
const ariaDescribedbyIds = `${messageAriaDescribedby ? 'input-message' : ''} ${error ? 'checkbox-error-message' : ''}`
.trim() || null; // Null so we don't add the attribute if we have an empty string
const ariaDescribedbyIds = [
messageAriaDescribedby ? 'input-message' : '',
error ? 'checkbox-error-message' : '',
hasDescription ? 'description' : '',
// Return null so we don't add the attribute if we have an empty string
].filter(Boolean).join(' ').trim() || null;
return (
<Host>
{description ?
{description ?
<legend id="description" class={descriptionClass}>{description}</legend> :
<slot name="description" />
}
Expand All @@ -201,7 +208,7 @@ export class VaCheckbox {
<span id="checkbox-error-message" role="alert">
{error && (
<Fragment>
<span class="usa-sr-only">{i18next.t('error')}</span>
<span class="usa-sr-only">{i18next.t('error')}</span>
<span class="usa-error-message">{error}</span>
</Fragment>
)}
Expand Down Expand Up @@ -231,15 +238,19 @@ export class VaCheckbox {
</Host>
);
} else {
const ariaDescribedbyIds = `${messageAriaDescribedby ? 'input-message' : ''} ${error ? 'error-message' : ''}`
.trim() || null; // Null so we don't add the attribute if we have an empty string
const ariaDescribedbyIds = [
messageAriaDescribedby ? 'input-message' : '',
error ? 'checkbox-error-message' : '',
hasDescription ? 'description' : '',
// Return null so we don't add the attribute if we have an empty string
].filter(Boolean).join(' ').trim() || null;
return (
<Host>
<div id="description">
{description ? <p>{description}</p> : <slot name="description" />}
</div>
{hint && <span class="hint-text">{hint}</span>}
<span id="error-message" role="alert">
<span id="checkbox-error-message" role="alert">
{error && (
<Fragment>
<span class="sr-only">{i18next.t('error')}</span> {error}
Expand Down

0 comments on commit 4c97574

Please sign in to comment.