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

User/aubreyquinn/ts converstion #132

Merged
merged 10 commits into from
Nov 8, 2024
Merged
Changes from 1 commit
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
Prev Previous commit
Next Next commit
added unit tests
aubreyquinn committed Nov 6, 2024
commit 458f8a36ba38a44b48d0c843ba42284d1994ba9d
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -122,7 +122,7 @@ Any use of third-party trademarks or logos are subject to those third-party's po
| [dialogbody-needs-title-content-and-actions](docs/rules/dialogbody-needs-title-content-and-actions.md) | A DialogBody should have a header(DialogTitle), content(DialogContent), and footer(DialogActions) | ✅ | | |
| [dialogsurface-needs-aria](docs/rules/dialogsurface-needs-aria.md) | DialogueSurface need accessible labelling: aria-describedby on DialogueSurface and aria-label or aria-labelledby(if DialogueTitle is missing) | ✅ | | |
| [dropdown-needs-labelling](docs/rules/dropdown-needs-labelling.md) | Accessibility: Dropdown menu must have an id and it needs to be linked via htmlFor of a Label | ✅ | | |
| [field-needs-labelling](docs/rules/field-needs-labelling.md) | Accessibility: Field must have either label, validationMessage and hint attributes | ✅ | | |
| [field-needs-labelling](docs/rules/field-needs-labelling.md) | Accessibility: Field must have label | ✅ | | |
| [image-button-missing-aria](docs/rules/image-button-missing-aria.md) | Accessibility: Image buttons must have accessible labelling: title, aria-label, aria-labelledby, aria-describedby | ✅ | | |
| [input-components-require-accessible-name](docs/rules/input-components-require-accessible-name.md) | Accessibility: Input fields must have accessible labelling: aria-label, aria-labelledby or an associated label | ✅ | | |
| [link-missing-labelling](docs/rules/link-missing-labelling.md) | Accessibility: Image links must have an accessible name. Add either text content, labelling to the image or labelling to the link itself. | ✅ | | 🔧 |
2 changes: 1 addition & 1 deletion docs/rules/field-needs-labelling.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Accessibility: Field must have label attribute (`@microsoft/fluentui-jsx-a11y/field-needs-labelling`)
# Accessibility: Field must have label (`@microsoft/fluentui-jsx-a11y/field-needs-labelling`)

💼 This rule is enabled in the ✅ `recommended` config.

Original file line number Diff line number Diff line change
@@ -5,12 +5,14 @@

const { hasNonEmptyProp } = require("../util/hasNonEmptyProp");
const elementType = require("jsx-ast-utils").elementType;
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
const rule = ESLintUtils.RuleCreator.withoutDocs({
defaultOptions: [],
meta: {
// possible error messages for the rule
messages: {
@@ -21,16 +23,17 @@ module.exports = {
// docs for the rule
docs: {
description: "Accessibility: Field must have label",
recommended: true,
recommended: "strict",
url: "https://www.w3.org/TR/html-aria/" // URL to the documentation page for this rule
},
schema: []
},

// create (function) returns an object with methods that ESLint calls to “visit” nodes while traversing the abstract syntax tree
create(context) {
return {
// visitor functions for different types of nodes
JSXOpeningElement(node) {
JSXOpeningElement(node: TSESTree.JSXOpeningElement) {
// if it is not a Spinner, return
if (elementType(node) !== "Field") {
return;
@@ -48,4 +51,6 @@ module.exports = {
}
};
}
};
});

export default rule;
76 changes: 0 additions & 76 deletions lib/rules/tablist-and-tabs-need-labelling.js

This file was deleted.

77 changes: 77 additions & 0 deletions lib/rules/tablist-and-tabs-need-labelling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
import { hasTextContentChild } from "../util/hasTextContentChild";
import { hasNonEmptyProp } from "../util/hasNonEmptyProp";
import { hasAssociatedLabelViaAriaLabelledBy } from "../util/labelUtils";
import { elementType } from "jsx-ast-utils";
import { JSXOpeningElement } from "estree-jsx";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

const rule = ESLintUtils.RuleCreator.withoutDocs({
defaultOptions: [],
meta: {
type: "problem",
docs: {
description:
"This rule aims to ensure that Tabs with icons but no text labels have an accessible name and that Tablist is properly labeled.",
recommended: "strict",
url: "https://www.w3.org/WAI/ARIA/apg/patterns/tabs/" // URL to the documentation page for this rule
},
fixable: undefined,
schema: [],
messages: {
missingTabLabel: "Accessibility: Tab elements must have an aria-label attribute is there is no visiable text content",
missingTablistLabel: "Accessibility: Tablist must have an accessible label"
}
},

create(context) {
return {
// visitor functions for different types of nodes
JSXOpeningElement(node: TSESTree.JSXOpeningElement) {
const elementTypeValue = elementType(node as unknown as JSXOpeningElement);

// if it is not a Tablist or Tab, return
if (elementTypeValue !== "Tablist" && elementTypeValue !== "Tab") {
return;
}

// Check for Tablist elements
if (elementTypeValue === "Tablist") {
if (
// if the Tablist has a label, if the Tablist has an associated label, return
hasNonEmptyProp(node.attributes, "aria-label") || //aria-label
hasAssociatedLabelViaAriaLabelledBy(node, context) // aria-labelledby
) {
return;
}
context.report({
node,
messageId: "missingTablistLabel"
});
}

// Check for Tab elements
if (elementTypeValue === "Tab") {
if (
hasTextContentChild(node.parent as unknown as TSESTree.JSXElement) || // text content
hasNonEmptyProp(node.attributes, "aria-label") // aria-label
) {
return;
}
context.report({
node,
messageId: "missingTabLabel"
});
}
}
};
}
});

export default rule;
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

"use strict";

var elementType = require("jsx-ast-utils").elementType;
const { hasNonEmptyProp } = require("../util/hasNonEmptyProp");
const { applicableComponents } = require("../applicableComponents/buttonBasedComponents");
import { hasNonEmptyProp } from "../util/hasNonEmptyProp";
import { applicableComponents } from "../applicableComponents/buttonBasedComponents";
import { ESLintUtils, TSESTree } from "@typescript-eslint/utils";
import { elementType } from "jsx-ast-utils";
import { JSXOpeningElement } from "estree-jsx";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
const rule = ESLintUtils.RuleCreator.withoutDocs({
defaultOptions: [],
meta: {
// possible warning messages for the lint rule
messages: {
@@ -21,19 +21,20 @@ module.exports = {
type: "suggestion", // `problem`, `suggestion`, or `layout`
docs: {
description: "Visual label is better than an aria-label",
recommended: true,
url: null // URL to the documentation page for this rule
recommended: "strict",
url: undefined // URL to the documentation page for this rule
},
fixable: null, // Or `code` or `whitespace`
fixable: undefined, // Or `code` or `whitespace`
schema: [] // Add a schema if the rule has options
},

// create (function) returns an object with methods that ESLint calls to “visit” nodes while traversing the abstract syntax tree
create(context) {
return {
// visitor functions for different types of nodes
JSXOpeningElement(node) {
JSXOpeningElement(node: TSESTree.JSXOpeningElement) {
// if it is not a listed component, return
if (!applicableComponents.includes(elementType(node))) {
if (!applicableComponents.includes(elementType(node as unknown as JSXOpeningElement))) {
return;
}

@@ -47,4 +48,6 @@ module.exports = {
}
};
}
};
});

export default rule;
6 changes: 4 additions & 2 deletions lib/util/flattenChildren.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
import { TSESTree } from "@typescript-eslint/types";

// Flatten the JSX tree structure by recursively collecting all child elements
export default function flattenChildren(node: TSESTree.JSXElement): TSESTree.JSXElement[] {
const flattenChildren = (node: TSESTree.JSXElement): TSESTree.JSXElement[] => {
const flatChildren: TSESTree.JSXElement[] = [];

if (node.children && node.children.length > 0) {
@@ -17,4 +17,6 @@ export default function flattenChildren(node: TSESTree.JSXElement): TSESTree.JSX
}

return flatChildren;
}
};

export { flattenChildren };
4 changes: 2 additions & 2 deletions lib/util/hasFieldParent.ts
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import { TSESLint } from "@typescript-eslint/utils";
import { JSXOpeningElement } from "estree-jsx";

// Function to check if the current node has a "Field" parent JSXElement
export function hasFieldParent(context: TSESLint.RuleContext<string, unknown[]>): boolean {
export const hasFieldParent = (context: TSESLint.RuleContext<string, unknown[]>): boolean => {
const ancestors: TSESTree.Node[] = context.getAncestors();

if (ancestors == null || ancestors.length === 0) {
@@ -28,4 +28,4 @@ export function hasFieldParent(context: TSESLint.RuleContext<string, unknown[]>)
});

return field;
}
};
40 changes: 0 additions & 40 deletions lib/util/hasLabelledChildImage.js

This file was deleted.

Loading