Commit 1b8ceab7 authored by David Sveningsson's avatar David Sveningsson

feat(rules): new rule `wcag/h71` requiring `<fieldset>` to have `<legend>`

parent fd67ad97
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`docs/rules/wcag/h71.md inline validation: correct 1`] = `Array []`;
exports[`docs/rules/wcag/h71.md inline validation: incorrect 1`] = `
Array [
Object {
"errorCount": 1,
"filePath": "inline",
"messages": Array [
Object {
"column": 2,
"context": undefined,
"line": 1,
"message": "<fieldset> must have a <legend> as the first child",
"offset": 1,
"ruleId": "WCAG/H71",
"selector": "fieldset",
"severity": 2,
"size": 8,
},
],
"source": "<fieldset>
...
</fieldset>",
"warningCount": 0,
},
]
`;
import HtmlValidate from "../../../../src/htmlvalidate";
const markup: { [key: string]: string } = {};
markup["incorrect"] = `<fieldset>
...
</fieldset>`;
markup["correct"] = `<fieldset>
<legend>Lorem ipsum</legend>
...
</fieldset>`;
describe("docs/rules/wcag/h71.md", () => {
it("inline validation: incorrect", () => {
const htmlvalidate = new HtmlValidate({"rules":{"wcag/h71":"error"}});
const report = htmlvalidate.validateString(markup["incorrect"]);
expect(report.results).toMatchSnapshot();
});
it("inline validation: correct", () => {
const htmlvalidate = new HtmlValidate({"rules":{"wcag/h71":"error"}});
const report = htmlvalidate.validateString(markup["correct"]);
expect(report.results).toMatchSnapshot();
});
});
---
docType: rule
name: wcag/h71
category: a17y
summary: "WCAG 2.1 H71: Providing a description for groups of form controls"
---
# WCAG 2.1 H71: Providing a description for groups of form controls (`wcag/h71`)
[WCAG 2.1 technique H71][1] requires all fieldsets to have a `<legend>` element as first child element.
[1]: https://www.w3.org/WAI/WCAG21/Techniques/html/H71
## Rule details
Examples of **incorrect** code for this rule:
<validate name="incorrect" rules="wcag/h71">
<fieldset>
...
</fieldset>
</validate>
Examples of **correct** code for this rule:
<validate name="correct" rules="wcag/h71">
<fieldset>
<legend>Lorem ipsum</legend>
...
</fieldset>
</validate>
......@@ -202,7 +202,7 @@ textual description of the content. E.g. it cannot suggest to use `<abbr>` or
</tr>
<tr>
<td class="table-right">H71</td>
<td>Providing a description for groups of form controls using fieldset and legend elements.<em>{@link rule:element-required-content} validates presence of <code>&lt;legend&gt;</code> inside <code>&lt;fieldset&gt;</code>, but not whenever <code>&lt;fieldset&gt;</code> itself is used.</em></td>
<td>Providing a description for groups of form controls using fieldset and legend elements.<em>Use {@link rule:wcag/h71} to validate presence of <code>&lt;legend&gt;</code> inside <code>&lt;fieldset&gt;</code> but it will not validate if <code>&lt;fieldset&gt;</code> itself is used.</em></td>
<td class="support-yes">Yes</td>
</tr>
<tr>
......
......@@ -1708,7 +1708,7 @@ exports[`HTML elements <embed> valid markup 1`] = `Array []`;
exports[`HTML elements <fieldset> invalid markup 1`] = `
Array [
Object {
"errorCount": 2,
"errorCount": 3,
"filePath": "test-files/elements/fieldset-invalid.html",
"messages": Array [
Object {
......@@ -1725,6 +1725,17 @@ Array [
"severity": 2,
"size": 8,
},
Object {
"column": 2,
"context": undefined,
"line": 2,
"message": "<fieldset> must have a <legend> as the first child",
"offset": 29,
"ruleId": "WCAG/H71",
"selector": "fieldset:nth-child(1)",
"severity": 2,
"size": 8,
},
Object {
"column": 3,
"context": undefined,
......
......@@ -41,5 +41,6 @@ module.exports = {
"wcag/h36": "error",
"wcag/h37": "error",
"wcag/h67": "error",
"wcag/h71": "error",
},
};
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`wcag/h71 should contain documentation 1`] = `
Object {
"description": "H71: Providing a description for groups of form controls using fieldset and legend elements",
"url": "https://html-validate.org/rules/h71.html",
}
`;
import HtmlValidate from "../../htmlvalidate";
import "../../matchers";
describe("wcag/h71", () => {
let htmlvalidate: HtmlValidate;
beforeAll(() => {
htmlvalidate = new HtmlValidate({
rules: { "wcag/h71": "error" },
});
});
it("should report error when <fieldset> is missing <legend>", () => {
const report = htmlvalidate.validateString("<fieldset></fieldset>");
expect(report).toBeInvalid();
expect(report).toHaveError(
"WCAG/H71",
"<fieldset> must have a <legend> as the first child"
);
});
it("should not report when <fieldset> have <legend>", () => {
const report = htmlvalidate.validateString(
"<fieldset><legend>foo</legend></fieldset>"
);
expect(report).toBeValid();
});
it("should not report when <fieldset> have multiple <legend>", () => {
const report = htmlvalidate.validateString(
"<fieldset><legend>foo</legend><legend>bar</legend></fieldset>"
);
expect(report).toBeValid();
});
it("should not report when <fieldset> have out-of-order <legend>", () => {
const report = htmlvalidate.validateString(
"<fieldset><div>foo</div><legend>bar</legend></fieldset>"
);
expect(report).toBeValid();
});
it("should contain documentation", () => {
htmlvalidate = new HtmlValidate({
rules: { "wcag/h71": "error" },
});
expect(htmlvalidate.getRuleDocumentation("wcag/h71")).toMatchSnapshot();
});
});
import { DOMReadyEvent } from "../../event";
import { Rule, RuleDocumentation, ruleDocumentationUrl } from "../../rule";
import { HtmlElement } from "../../dom";
class H71 extends Rule {
public documentation(): RuleDocumentation {
return {
description:
"H71: Providing a description for groups of form controls using fieldset and legend elements",
url: ruleDocumentationUrl(__filename),
};
}
public constructor(options: void) {
super(options);
this.name = "WCAG/H71";
}
public setup(): void {
this.on("dom:ready", (event: DOMReadyEvent) => {
const { document } = event;
const fieldsets = document.querySelectorAll("fieldset");
for (const fieldset of fieldsets) {
this.validate(fieldset);
}
});
}
private validate(fieldset: HtmlElement): void {
const legend = fieldset.querySelectorAll("> legend");
if (legend.length === 0) {
this.reportNode(fieldset);
}
}
private reportNode(node: HtmlElement): void {
super.report(
node,
`${node.annotatedName} must have a <legend> as the first child`
);
}
}
module.exports = H71;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment