Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for heading role elements #158

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 59 additions & 14 deletions plugins/headings/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ let annotate = require("../shared/annotate")("headings");
let outlineItemTemplate = require("./outline-item.handlebars");
require("./style.less");

class HeadingLevel {
/*
* Constructs a heading level from a heading element (h1 - h6) or
* any element with a heading role and an aria level (potentially >6).
*/
constructor($el) {
let tagName = $el.prop("tagName").toLowerCase();
if ($el.attr("role") === "heading"
&& typeof $el.attr("aria-level") !== "undefined") {
this._level = +$el.attr("aria-level");
this._tag = `<${tagName} role="heading" aria-level="${this._level}">`
} else {
this._level = +tagName.slice(1);
this._tag = `<${tagName}>`
}
}

tag() {
return this._tag;
}

value() {
return this._level;
}
}

const ERRORS = {
FIRST_NOT_H1(level) {
return {
Expand All @@ -18,7 +44,7 @@ const ERRORS = {
To give your document a proper structure for assistive
technologies, it is important to lay out your headings
beginning with an <code>&lt;h1&gt;</code>. We found an
<code>&lt;h${level}&gt;</code> instead.
<code>${level.tag()}</code> instead.
</div>
`
};
Expand All @@ -41,27 +67,36 @@ const ERRORS = {

// This error accepts two arguments to display a relevant error message
NONCONSECUTIVE_HEADER(prevLevel, currLevel) {
let _tag = (level) => `<code>&lt;h${level}&gt;</code>`;
let levelToTagCode = (level) => {
if (level <= 6) {
return `<code>&lt;h${level}&gt;</code>`;
}
return `<code>&lt;div role="heading" aria-level="${level}"&gt;>`;
}
let _tag = (level) => `<code>${level.tag()}</code>`;

let description = `
<div>
This document contains an ${_tag(currLevel)} tag directly
following an ${_tag(prevLevel)}. In order to maintain a consistent
outline of the page for assistive technologies, reduce the gap in
the heading level by upgrading this tag to an
${_tag(prevLevel+1)}`;
${levelToTagCode(prevLevel.value()+1)}`;

// Suggest upgrading the tag to the same level as `prevLevel` iff
// `prevLevel` is not 1
if (prevLevel !== 1) {
description += ` or ${_tag(prevLevel)}`;
if (prevLevel.value() !== 1) {
description += ` or ${levelToTagCode(prevLevel.value())}`;
}

description += ".</div>";

return {
// just convert to hX for title as space is constrained even
// if actual tag is not h1-h6
title: `
Nonconsecutive heading level used (h${prevLevel} &rarr;
h${currLevel})
Nonconsecutive heading level used (h${prevLevel.value()} &rarr;
h${currLevel.value()})
`,
description: description
};
Expand Down Expand Up @@ -89,24 +124,29 @@ class HeadingsPlugin extends Plugin {
let prevLevel;
$headings.each((i, el) => {
let $el = $(el);
let level = +$el.prop("tagName").slice(1);
let level = new HeadingLevel($el);
let error;

// Check for any violations
// NOTE: These violations do not overlap, but as we add more, we
// may want to separate the conditionals here to report multiple
// errors on the same tag.
if (i === 0 && level !== 1) {
if (i === 0 && level.value() !== 1) {
error = ERRORS.FIRST_NOT_H1(level); // eslint-disable-line new-cap
} else if (prevLevel && level - prevLevel > 1) {
} else if (prevLevel.value()
&& level.value() - prevLevel.value() > 1) {
error = ERRORS.NONCONSECUTIVE_HEADER(prevLevel, level); // eslint-disable-line new-cap
}

prevLevel = level;

// Render the entry in the outline for the "Summary" tab
let $item = $(outlineItemTemplate({
level: level,
// provide a numerical level which can go arbitarily high
// and a capped level which we use for styling the element
// that stops after 6.
level: level.value(),
_level: level.value() <= 6 ? level.value() : "higher",
text: $el.text()
}));

Expand All @@ -123,7 +163,9 @@ class HeadingsPlugin extends Plugin {
// Place an error label on the heading tag
annotate.errorLabel(
$el,
$el.prop("tagName").toLowerCase(),
// just convert to hX for title as space is constrained even
// if actual tag is not h1-h6
`h${level.value()}`,
error.title,
infoPanelError);

Expand All @@ -135,7 +177,8 @@ class HeadingsPlugin extends Plugin {
.addClass("tota11y-label-error");
} else {
// Label the heading tag
annotate.label($el).addClass("tota11y-label-success");
annotate.label($el, `h${level.value()}`)
.addClass("tota11y-label-success");

// Mark the summary item as green
$item
Expand All @@ -148,7 +191,9 @@ class HeadingsPlugin extends Plugin {
}

run() {
let $headings = $("h1, h2, h3, h4, h5, h6");
let $headings = $(
"h1, h2, h3, h4, h5, h6, [role=\"heading\"][aria-level]"
);
// `this.outline` has the side-effect of also reporting violations
let $items = this.outline($headings);

Expand Down
2 changes: 1 addition & 1 deletion plugins/headings/outline-item.handlebars
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="tota11y-heading-outline-entry heading-level-{{level}}">
<div class="tota11y-heading-outline-entry heading-level-{{_level}}">
<span class="tota11y-heading-outline-level tota11y-label">{{level}}</span>
<span class="tota11y-heading-outline-heading-text">{{text}}</span>
</div>
3 changes: 3 additions & 0 deletions plugins/headings/style.less
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
&.heading-level-6 {
margin-left: 100px;
}
&.heading-level-higher {
margin-left: 100px;
}

}

Expand Down
4 changes: 2 additions & 2 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ <h2>Heading</h2>
<p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
</div>
<div class="col-md-4">
<h2>Heading</h2>
<div role="heading" aria-level="2">ARIA Heading</div>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
<p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
</div>
Expand All @@ -137,7 +137,7 @@ <h2>Heading</h2>
<p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
</div>
<div class="col-md-4">
<h2>Heading</h2>
<div role="heading" aria-level="7">Invalid ARIA Heading</div>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p>
<p><a class="btn btn-default" href="#" role="button">View details &raquo;</a></p>
</div>
Expand Down