Skip to content

Commit 209b736

Browse files
authored
Merge pull request #112 from microsoft/ajayagr/migrate-repo-to-ts
chore: migrate repo to use ts
2 parents 65929e1 + 2adb8fa commit 209b736

File tree

83 files changed

+8582
-1765
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+8582
-1765
lines changed

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@ module.exports = {
2424
],
2525
rules: {
2626
"header/header": [2, "line", [" Copyright (c) Microsoft Corporation.", " Licensed under the MIT License."], 2]
27-
}
27+
},
28+
ignorePatterns: ["node_modules", "dist/"]
2829
};

.github/workflows/node.js.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
strategy:
1717
matrix:
18-
node-version: [14.x, 16.x, 18.x]
18+
node-version: [16.x, 18.x, 20.x]
1919
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
2020

2121
steps:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const applicableComponents: string[];
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use strict";
2+
// Copyright (c) Microsoft Corporation.
3+
// Licensed under the MIT License.
4+
const applicableComponents = ["Button", "ToggleButton", "CompoundButton"];
5+
module.exports = {
6+
applicableComponents
7+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const applicableComponents: string[];
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use strict";
2+
// Copyright (c) Microsoft Corporation.
3+
// Licensed under the MIT License.
4+
const applicableComponents = ["Input", "Slider", "DatePicker", "Textarea", "TextField", "TimePicker", "SearchBox", "Select"];
5+
module.exports = {
6+
applicableComponents
7+
};

dist/lib/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {};

lib/index.js renamed to dist/lib/index.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1+
"use strict";
12
// Copyright (c) Microsoft Corporation.
23
// Licensed under the MIT License.
3-
4-
"use strict";
4+
var __importDefault = (this && this.__importDefault) || function (mod) {
5+
return (mod && mod.__esModule) ? mod : { "default": mod };
6+
};
7+
Object.defineProperty(exports, "__esModule", { value: true });
58
console.log("Loading my-eslint-plugin");
9+
const prefer_aria_over_title_attribute_1 = __importDefault(require("./rules/prefer-aria-over-title-attribute"));
610
//------------------------------------------------------------------------------
711
// Requirements
812
//------------------------------------------------------------------------------
9-
1013
//------------------------------------------------------------------------------
1114
// Plugin Definition
1215
//------------------------------------------------------------------------------
13-
1416
// import all rules in lib/rules
1517
module.exports = {
1618
rules: {
@@ -35,7 +37,7 @@ module.exports = {
3537
"avatar-needs-name": require("./rules/avatar-needs-name"),
3638
"radio-button-missing-label": require("./rules/radio-button-missing-label"),
3739
"radiogroup-missing-label": require("./rules/radiogroup-missing-label"),
38-
"prefer-aria-over-title-attribute": require("./rules/prefer-aria-over-title-attribute"),
40+
"prefer-aria-over-title-attribute": prefer_aria_over_title_attribute_1.default,
3941
"dialogbody-needs-title-content-and-actions": require("./rules/dialogbody-needs-title-content-and-actions"),
4042
"dialogsurface-needs-aria": require("./rules/dialogsurface-needs-aria"),
4143
"spinner-needs-labelling": require("./rules/spinner-needs-labelling"),
@@ -75,9 +77,7 @@ module.exports = {
7577
}
7678
}
7779
};
78-
7980
// import processors
8081
module.exports.processors = {
81-
// add your processors here
82+
// add your processors here
8283
};
83-
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare const _exports: import("eslint").Rule.RuleModule;
2+
export = _exports;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
"use strict";
4+
const { hasNonEmptyProp } = require("../util/hasNonEmptyProp");
5+
const { hasToolTipParent } = require("../util/hasTooltipParent");
6+
const { hasTextContentChild } = require("../util/hasTextContentChild");
7+
const { hasAssociatedLabelViaAriaLabelledBy } = require("../util/labelUtils");
8+
var hasProp = require("jsx-ast-utils").hasProp;
9+
var elementType = require("jsx-ast-utils").elementType;
10+
//------------------------------------------------------------------------------
11+
// Rule Definition
12+
//------------------------------------------------------------------------------
13+
/** @type {import('eslint').Rule.RuleModule} */
14+
module.exports = {
15+
meta: {
16+
messages: {
17+
missingAriaLabel: "Accessibility: the accordion header must have an accessible name"
18+
},
19+
type: "problem", // `problem`, `suggestion`, or `layout`
20+
docs: {
21+
description: "The accordion header is a button and it needs an accessibile name e.g. text content, aria-label, aria-labelledby.",
22+
recommended: false,
23+
url: null // URL to the documentation page for this rule
24+
},
25+
fixable: null, // Or `code` or `whitespace`
26+
schema: [] // Add a schema if the rule has options
27+
},
28+
// create (function) returns an object with methods that ESLint calls to “visit” nodes while traversing the abstract syntax tree
29+
create(context) {
30+
return {
31+
// visitor functions for different types of nodes
32+
JSXElement(node) {
33+
const openingElement = node.openingElement;
34+
// if it is not a AccordionHeader, return
35+
if (elementType(openingElement) !== "AccordionHeader") {
36+
return;
37+
}
38+
// if it has text content, return
39+
if (hasTextContentChild(node)) {
40+
return;
41+
}
42+
// if it is not an icon button, return
43+
if (!hasProp(openingElement.attributes, "icon") && !hasProp(openingElement.attributes, "expandIcon")) {
44+
return;
45+
}
46+
// if it has a tooltip parent, return
47+
if (hasToolTipParent(context)) {
48+
return;
49+
}
50+
// the button has an associated label
51+
if (hasAssociatedLabelViaAriaLabelledBy(openingElement, context)) {
52+
return;
53+
}
54+
const hasAccessibleLabelling = hasNonEmptyProp(openingElement.attributes, "title") || hasNonEmptyProp(openingElement.attributes, "aria-label");
55+
// if it has no accessible name, report error
56+
if (!hasAccessibleLabelling) {
57+
context.report({
58+
node,
59+
messageId: `missingAriaLabel`
60+
});
61+
}
62+
}
63+
};
64+
}
65+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare const _exports: import("eslint").Rule.RuleModule;
2+
export = _exports;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
"use strict";
4+
//------------------------------------------------------------------------------
5+
// Rule Definition
6+
//------------------------------------------------------------------------------
7+
/** @type {import('eslint').Rule.RuleModule} */
8+
module.exports = {
9+
meta: {
10+
messages: {
11+
accordionItemOneHeaderOnePanel: "ensure AccordionItem has exactly one header and one panel"
12+
},
13+
type: "problem", // `problem`, `suggestion`, or `layout`
14+
docs: {
15+
description: "An AccordionItem needs exactly one header and one panel",
16+
recommended: true,
17+
url: "https://www.w3.org/WAI/ARIA/apg/patterns/accordion/" // URL to the documentation page for this rule
18+
},
19+
fixable: null, // Or `code` or `whitespace`
20+
schema: [] // Add a schema if the rule has options
21+
},
22+
create(context) {
23+
return {
24+
JSXOpeningElement(node) {
25+
if (node.name.name !== "AccordionItem") {
26+
return;
27+
}
28+
const children = node.parent.children.filter(child => child.type === "JSXElement");
29+
const hasOneHeader = children.filter(child => child.openingElement.name.name === "AccordionHeader").length === 1;
30+
const hasOnePanel = children.filter(child => child.openingElement.name.name === "AccordionPanel").length === 1;
31+
if (!hasOneHeader || !hasOnePanel || children.length !== 2) {
32+
context.report({
33+
node,
34+
messageId: "accordionItemOneHeaderOnePanel"
35+
});
36+
}
37+
}
38+
};
39+
}
40+
};

dist/lib/rules/avatar-needs-name.d.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export namespace meta {
2+
namespace messages {
3+
let missingAriaLabel: string;
4+
}
5+
let type: string;
6+
namespace docs {
7+
let description: string;
8+
let recommended: boolean;
9+
let url: string;
10+
}
11+
let schema: never[];
12+
}
13+
export function create(context: any): {
14+
JSXOpeningElement(node: any): void;
15+
};

dist/lib/rules/avatar-needs-name.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
"use strict";
4+
const { hasNonEmptyProp } = require("../util/hasNonEmptyProp");
5+
var elementType = require("jsx-ast-utils").elementType;
6+
const { hasAssociatedLabelViaAriaLabelledBy } = require("../util/labelUtils");
7+
//------------------------------------------------------------------------------
8+
// Rule Definition
9+
//------------------------------------------------------------------------------
10+
module.exports = {
11+
meta: {
12+
// possible error messages for the rule
13+
messages: {
14+
missingAriaLabel: "Accessibility: Avatar must have an accessible name"
15+
},
16+
// "problem" means the rule is identifying code that either will cause an error or may cause a confusing behavior: https://eslint.org/docs/latest/developer-guide/working-with-rules
17+
type: "problem",
18+
// docs for the rule
19+
docs: {
20+
// DONE
21+
description: "Accessibility: Avatar must have an accessible labelling: name, aria-label, aria-labelledby",
22+
recommended: true,
23+
url: "https://www.w3.org/TR/html-aria/" // URL to the documentation page for this rule
24+
},
25+
schema: []
26+
},
27+
// create (function) returns an object with methods that ESLint calls to “visit” nodes while traversing the abstract syntax tree
28+
create(context) {
29+
return {
30+
// visitor functions for different types of nodes
31+
JSXOpeningElement(node) {
32+
// if it is not an Avatar, return
33+
if (elementType(node) !== "Avatar") {
34+
return;
35+
}
36+
// if the Avatar has a name, aria-label or aria-labelledby, return
37+
if (hasNonEmptyProp(node.attributes, "name") ||
38+
hasNonEmptyProp(node.attributes, "aria-label") ||
39+
hasAssociatedLabelViaAriaLabelledBy(node, context)) {
40+
return;
41+
}
42+
// no aria
43+
context.report({
44+
node,
45+
messageId: `missingAriaLabel`
46+
});
47+
}
48+
};
49+
}
50+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare const _exports: import("eslint").Rule.RuleModule;
2+
export = _exports;
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
"use strict";
4+
const { applicableComponents: inputComponents } = require("../applicableComponents/inputBasedComponents");
5+
const { applicableComponents: buttonComponents } = require("../applicableComponents/buttonBasedComponents");
6+
const { elementType } = require("jsx-ast-utils");
7+
const { isInsideLabelTag, hasAssociatedLabelViaHtmlFor, hasAssociatedLabelViaAriaLabelledBy, hasAssociatedLabelViaAriaDescribedby } = require("../util/labelUtils");
8+
const { hasFieldParent } = require("../util/hasFieldParent");
9+
const { hasNonEmptyProp } = require("../util/hasNonEmptyProp");
10+
const { hasToolTipParent } = require("../util/hasTooltipParent");
11+
const { hasTextContentChild } = require("../util/hasTextContentChild");
12+
//------------------------------------------------------------------------------
13+
// Rule Definition
14+
//------------------------------------------------------------------------------
15+
/** @type {import('eslint').Rule.RuleModule} */
16+
module.exports = {
17+
meta: {
18+
messages: {
19+
noAriaDescribedbyAsLabel: "Accessibility: aria-describedby provides additional context and is not meant for primary labeling."
20+
},
21+
type: "suggestion", // `problem`, `suggestion`, or `layout`
22+
docs: {
23+
description: "aria-describedby provides additional context and is not meant for primary labeling.",
24+
recommended: true,
25+
url: null // URL to the documentation page for this rule
26+
},
27+
fixable: null, // Or `code` or `whitespace`
28+
schema: [] // Add a schema if the rule has options
29+
},
30+
create(context) {
31+
return {
32+
JSXElement(node) {
33+
const openingElement = node.openingElement;
34+
if (buttonComponents.includes(elementType(openingElement)) && // It's a button-based component
35+
!hasToolTipParent(context) && // It doesn't have a tooltip parent
36+
!hasTextContentChild(node) && // It doesn't have text content
37+
!hasNonEmptyProp(openingElement.attributes, "title") && // Doesn't have a title
38+
!hasNonEmptyProp(openingElement.attributes, "aria-label") && // Doesn't have an aria-label
39+
!hasAssociatedLabelViaAriaLabelledBy(openingElement, context) && // Doesn't have aria-labelledby
40+
hasAssociatedLabelViaAriaDescribedby(openingElement, context) // But it does have aria-describedby
41+
) {
42+
context.report({
43+
node,
44+
messageId: "noAriaDescribedbyAsLabel"
45+
});
46+
}
47+
if (inputComponents.includes(elementType(openingElement)) && // It's an input component
48+
!hasFieldParent(context) && // It doesn't have a field parent
49+
!isInsideLabelTag(context) && // It's not inside a label tag
50+
!hasAssociatedLabelViaHtmlFor(openingElement, context) && // Doesn't have a label via htmlFor
51+
!hasAssociatedLabelViaAriaLabelledBy(openingElement, context) && // Doesn't have aria-labelledby
52+
hasAssociatedLabelViaAriaDescribedby(openingElement, context) // But it does have aria-describedby
53+
) {
54+
context.report({
55+
node,
56+
messageId: "noAriaDescribedbyAsLabel"
57+
});
58+
}
59+
}
60+
};
61+
}
62+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare const _exports: import("eslint").Rule.RuleModule;
2+
export = _exports;

0 commit comments

Comments
 (0)