Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/rules/atomic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class AtomicRule extends ValidationRule {
type = ValidationRuleType.Error;
name = "atomic";
anchored = true;
groupName = "Atomic violation";

accept(element: HTMLElement): boolean {
return matchesSelector(element, focusableElementSelector);
Expand Down
1 change: 1 addition & 0 deletions src/rules/badfocus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class BadFocusRule extends ValidationRule {
type = ValidationRuleType.Error;
name = "bad-focus";
anchored = false;
groupName = "Bad focus";

private _lastFocusStack: string[] | undefined;
private _lastBlurStack: string[] | undefined;
Expand Down
6 changes: 6 additions & 0 deletions src/rules/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ export abstract class ValidationRule<
*/
abstract anchored: boolean;

/**
* A short friendly group name for grouping issues in the UI.
* If undefined, the issue will not be grouped.
*/
groupName: string | undefined;

/**
* Window is set when the rule is added to the AbleDOM instance.
*/
Expand Down
1 change: 1 addition & 0 deletions src/rules/contrast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export class ContrastRule extends ValidationRule {
type = ValidationRuleType.Error;
name = "ContrastRule";
anchored = true;
groupName = "Insufficient color contrast";

accept(element: HTMLElement): boolean {
if (!isElementVisible(element)) {
Expand Down
1 change: 1 addition & 0 deletions src/rules/existingid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export class ExistingIdRule extends ValidationRule {
type = ValidationRuleType.Error;
name = "existing-id";
anchored = true;
groupName = "Referenced element not found";

accept(element: HTMLElement): boolean {
return (
Expand Down
1 change: 1 addition & 0 deletions src/rules/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export class FindElementRule extends ValidationRule {
type = ValidationRuleType.Warning;
name = "find-element";
anchored = true;
groupName = "Element found";

private _conditions: { [name: string]: (element: HTMLElement) => boolean } =
{};
Expand Down
1 change: 1 addition & 0 deletions src/rules/focuslost.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class FocusLostRule extends ValidationRule<BlurIssue> {
type = ValidationRuleType.Error;
name = "focus-lost";
anchored = false;
groupName = "Focus lost";

private _focusLostTimeout = 2000; // For now reporting lost focus after 2 seconds of it being lost.
private _clearScheduledFocusLost: (() => void) | undefined;
Expand Down
1 change: 1 addition & 0 deletions src/rules/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class FocusableElementLabelRule extends ValidationRule {
type = ValidationRuleType.Error;
name = "FocusableElementLabelRule";
anchored = true;
groupName = "Missing text label";

private _isAriaHidden(element: HTMLElement): boolean {
return element.ownerDocument.evaluate(
Expand Down
1 change: 1 addition & 0 deletions src/rules/nestedInteractive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class NestedInteractiveElementRule extends ValidationRule {
type = ValidationRuleType.Error;
name = "NestedInteractiveElementRule";
anchored = true;
groupName = "Nested interactive element";

private _isAriaHidden(element: HTMLElement): boolean {
return element.ownerDocument.evaluate(
Expand Down
1 change: 1 addition & 0 deletions src/rules/requiredparent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class RequiredParentRule extends ValidationRule {
type = ValidationRuleType.Error;
name = "aria-required-parent";
anchored = true;
groupName = "Missing required parent";

private parentRequirements: Map<string, ParentRequirement> = new Map([
[
Expand Down
1 change: 1 addition & 0 deletions src/rules/tabindex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export class TabIndexRule extends ValidationRule {
type = ValidationRuleType.Warning;
name = "tabindex";
anchored = true;
groupName = "Positive tabindex";

accept(element: HTMLElement): boolean {
return element.hasAttribute("tabindex");
Expand Down
5 changes: 5 additions & 0 deletions src/ui/chevrondown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/ui/chevronright.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
52 changes: 28 additions & 24 deletions src/ui/domBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export interface TextNodeWithAbleDOMUIFlag extends Text {

export class DOMBuilder {
private _doc: Document | undefined;
private _stack: (HTMLElement | DocumentFragment)[];
private _stack: (HTMLElement | DocumentFragment | null)[];

constructor(parent: HTMLElement | DocumentFragment) {
this._doc = parent.ownerDocument;
Expand All @@ -27,34 +27,37 @@ export class DOMBuilder {
attributes?: Record<string, string>,
callback?: (element: HTMLElement) => void,
namespace?: string,
skip?: boolean,
): DOMBuilder {
const parent = this._stack[0];
const element = (
namespace
? this._doc?.createElementNS(namespace, tagName)
: this._doc?.createElement(tagName)
) as HTMLElementWithAbleDOMUIFlag;

if (parent && element) {
element.__abledomui = true;

if (attributes) {
for (const [key, value] of Object.entries(attributes)) {
if (key === "class") {
element.className = value;
} else if (key === "style") {
element.style.cssText = value;
} else {
element.setAttribute(key, value);
const element = skip
? null
: ((namespace
? this._doc?.createElementNS(namespace, tagName)
: this._doc?.createElement(tagName)) as HTMLElementWithAbleDOMUIFlag);

if (parent !== undefined) {
if (parent && element) {
element.__abledomui = true;

if (attributes) {
for (const [key, value] of Object.entries(attributes)) {
if (key === "class") {
element.className = value;
} else if (key === "style") {
element.style.cssText = value;
} else {
element.setAttribute(key, value);
}
}
}
}

if (callback) {
callback(element);
}
if (callback) {
callback(element);
}

parent.appendChild(element);
parent.appendChild(element);
}

this._stack.unshift(element);
}
Expand Down Expand Up @@ -87,7 +90,8 @@ export class DOMBuilder {
element(
callback: (element: HTMLElement | DocumentFragment) => void,
): DOMBuilder {
callback(this._stack[0]);
const el = this._stack[0];
el && callback(el);
return this;
}
}
Loading
Loading