From 969864b4972e4e03d5780932978cddb2b934df4e Mon Sep 17 00:00:00 2001 From: Bea Esguerra Date: Thu, 6 Feb 2025 09:37:15 -0700 Subject: [PATCH] Extend initial a11y config for eslint (#2459) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary: This work is part of the implementation of [ADR#781 Enabling more lint rules for accessibility](https://khanacademy.atlassian.net/wiki/x/IoBVyg) These changes includes: - Updated dev dependency: `@khanacademy/eslint-config` - Extending `@khanacademy/eslint-config/a11y` config in project's eslint config - Currently, the a11y config enables the `aphrodite-add-style-variable-name` rule from `@khanacademy/eslint-plugin`. This is the first step to adding more a11y linting rules so that the naming convention for html elements is more predictable for [custom component mapping](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#component-mapping) for `eslint-plugin-jsx-a11y` (see [rule docs](https://github.com/Khan/wonder-stuff/blob/main/packages/eslint-plugin-khan/docs/aphrodite-add-style-variable-name.md) for more details). - `@khanacademy/eslint-config/a11y` will be updated in another PR to include config for `eslint-plugin-jsx-a11y` - Addressing lint errors from the aphrodite-add-style-variable-name rule Issue: FEI-6050 Implementation Plan: - https://github.com/Khan/wonder-stuff/pull/1114 Set up eslint a11y config and enable aphrodite-add-style-variable-name - (includes this PR) Use this new config in WB, perseus, webapp and address lint errors from the aphrodite-add-style-variable-name rule - Update the a11y.js config with the config based on the accessibility linting rules ADR - Use the updated config in projects and address existing errors by disabling the rules per line. Teams can address existing errors as they work in the area and new errors can be prevented with these lint rules! ## Test plan: - Run `pnpm lint` and make sure there are no linting errors. - Some variables were renamed internally, there should be no functional changes Author: beaesguerra Reviewers: jandrade Required Reviewers: Approved By: jandrade Checks: ✅ Chromatic - Get results on regular PRs (ubuntu-latest, 20.x), ✅ Lint / Lint (ubuntu-latest, 20.x), ✅ Test / Test (ubuntu-latest, 20.x, 2/2), ✅ Test / Test (ubuntu-latest, 20.x, 1/2), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Chromatic - Build and test on regular PRs / chromatic (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ⏭️ Chromatic - Skip on Release PR (changesets), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ gerald, ⏭️ dependabot Pull Request URL: https://github.com/Khan/wonder-blocks/pull/2459 --- .changeset/chatty-parents-swim.md | 15 +++++++++++++ .eslintrc.js | 1 + __docs__/components/token-table.tsx | 22 +++++++++---------- package.json | 2 +- .../src/components/accordion.tsx | 6 ++--- .../src/components/breadcrumbs-item.tsx | 6 ++--- .../src/components/breadcrumbs.tsx | 6 ++--- .../src/components/button-core.tsx | 6 ++--- .../src/components/clickable.tsx | 6 ++--- .../src/util/add-styles.typestest.tsx | 6 ++--- .../src/components/option-item.tsx | 6 ++--- .../src/components/text-area.tsx | 4 ++-- .../src/components/icon-button-core.tsx | 6 ++--- .../src/components/phosphor-icon.tsx | 4 ++-- .../src/components/link-core.tsx | 6 ++--- .../src/components/popover-content.tsx | 4 ++-- pnpm-lock.yaml | 10 ++++----- 17 files changed, 66 insertions(+), 50 deletions(-) create mode 100644 .changeset/chatty-parents-swim.md diff --git a/.changeset/chatty-parents-swim.md b/.changeset/chatty-parents-swim.md new file mode 100644 index 0000000000..7215140bfb --- /dev/null +++ b/.changeset/chatty-parents-swim.md @@ -0,0 +1,15 @@ +--- +"@khanacademy/wonder-blocks-breadcrumbs": patch +"@khanacademy/wonder-blocks-icon-button": patch +"@khanacademy/wonder-blocks-accordion": patch +"@khanacademy/wonder-blocks-clickable": patch +"@khanacademy/wonder-blocks-dropdown": patch +"@khanacademy/wonder-blocks-popover": patch +"@khanacademy/wonder-blocks-button": patch +"@khanacademy/wonder-blocks-core": patch +"@khanacademy/wonder-blocks-form": patch +"@khanacademy/wonder-blocks-icon": patch +"@khanacademy/wonder-blocks-link": patch +--- + +Update internal addStyle variable name to address aphrodite-add-style-variable-name linting rule diff --git a/.eslintrc.js b/.eslintrc.js index 4625e1f994..5ef6c5dda6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,6 +2,7 @@ module.exports = { extends: [ "@khanacademy", + "@khanacademy/eslint-config/a11y", // This config includes rules from @testing-library/jest-dom as well "plugin:testing-library/react", // This config includes rules from storybook to enforce story best diff --git a/__docs__/components/token-table.tsx b/__docs__/components/token-table.tsx index af9045a0ec..9068b374b8 100644 --- a/__docs__/components/token-table.tsx +++ b/__docs__/components/token-table.tsx @@ -5,9 +5,9 @@ import {addStyle} from "@khanacademy/wonder-blocks-core"; import {color, spacing} from "@khanacademy/wonder-blocks-tokens"; const StyledTable = addStyle("table"); -const StyledTableRow = addStyle("tr"); -const StyledTableHeader = addStyle("th"); -const StyledTableCell = addStyle("td"); +const StyledTr = addStyle("tr"); +const StyledTh = addStyle("th"); +const StyledTd = addStyle("td"); type Column = { // The label to display in the table header. @@ -44,26 +44,26 @@ export default function TokenTable({ return ( - + {columns.map((column, i) => ( - + {column.label} - + ))} - + {data.map((row: any, idx) => ( - + {columns.map((column, i) => ( - + {/* We pass the value directly or via the result of a function call. */} {typeof column.cell === "string" ? row[column.cell] : column.cell(row)} - + ))} - + ))} diff --git a/package.json b/package.json index 400617e348..db9a0ac837 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@babel/preset-typescript": "^7.24.1", "@changesets/cli": "^2.27.12", "@jest/globals": "^29.7.0", - "@khanacademy/eslint-config": "^5.0.1", + "@khanacademy/eslint-config": "^5.1.0", "@khanacademy/eslint-plugin": "^3.1.1", "@khanacademy/wonder-stuff-testing": "^3.0.5", "@rollup/plugin-babel": "^6.0.4", diff --git a/packages/wonder-blocks-accordion/src/components/accordion.tsx b/packages/wonder-blocks-accordion/src/components/accordion.tsx index 29218bd81b..3c6b8cd0a3 100644 --- a/packages/wonder-blocks-accordion/src/components/accordion.tsx +++ b/packages/wonder-blocks-accordion/src/components/accordion.tsx @@ -6,7 +6,7 @@ import type {AriaProps, StyleType} from "@khanacademy/wonder-blocks-core"; import AccordionSection from "./accordion-section"; -const StyledUnorderedList = addStyle("ul"); +const StyledUl = addStyle("ul"); export type AccordionCornerKindType = | "square" @@ -231,7 +231,7 @@ const Accordion = React.forwardRef(function Accordion( }; return ( - ); })} - + ); }); diff --git a/packages/wonder-blocks-breadcrumbs/src/components/breadcrumbs-item.tsx b/packages/wonder-blocks-breadcrumbs/src/components/breadcrumbs-item.tsx index ed1abcfc62..4fbc529647 100644 --- a/packages/wonder-blocks-breadcrumbs/src/components/breadcrumbs-item.tsx +++ b/packages/wonder-blocks-breadcrumbs/src/components/breadcrumbs-item.tsx @@ -23,7 +23,7 @@ type Props = AriaProps & { testId?: string; }; -const StyledListItem = addStyle("li"); +const StyledLi = addStyle("li"); const StyledSvg = addStyle("svg"); /** @@ -54,7 +54,7 @@ const BreadcrumbsItem = React.forwardRef(function BreadcrumbsItem( }; return ( - {children} {showSeparator && _renderSeparator()} - + ); }); diff --git a/packages/wonder-blocks-breadcrumbs/src/components/breadcrumbs.tsx b/packages/wonder-blocks-breadcrumbs/src/components/breadcrumbs.tsx index 56cfbc781e..6182693fd0 100644 --- a/packages/wonder-blocks-breadcrumbs/src/components/breadcrumbs.tsx +++ b/packages/wonder-blocks-breadcrumbs/src/components/breadcrumbs.tsx @@ -24,7 +24,7 @@ type Props = AriaProps & { testId?: string; }; -const StyledList = addStyle("ol"); +const StyledOl = addStyle("ol"); /** * A breadcrumb trail consists of a list of links to the parent pages @@ -85,7 +85,7 @@ const Breadcrumbs = React.forwardRef(function Breadcrumbs( data-testid={testId} ref={ref} > - + {React.Children.map(children, (item, index) => { const isLastChild = index === lastChildIndex; @@ -95,7 +95,7 @@ const Breadcrumbs = React.forwardRef(function Breadcrumbs( ["aria-current"]: isLastChild ? "page" : undefined, }); })} - + ); }); diff --git a/packages/wonder-blocks-button/src/components/button-core.tsx b/packages/wonder-blocks-button/src/components/button-core.tsx index 8129dea4b5..5fcddb1325 100644 --- a/packages/wonder-blocks-button/src/components/button-core.tsx +++ b/packages/wonder-blocks-button/src/components/button-core.tsx @@ -23,7 +23,7 @@ import {ButtonIcon} from "./button-icon"; type Props = SharedProps & ChildrenProps & ClickableState; -const StyledAnchor = addStyle("a"); +const StyledA = addStyle("a"); const StyledButton = addStyle("button"); const StyledLink = addStyle(Link); @@ -205,13 +205,13 @@ const ButtonCore: React.ForwardRefExoticComponent< {contents} ) : ( - } > {contents} - + ); } else { return ( diff --git a/packages/wonder-blocks-clickable/src/components/clickable.tsx b/packages/wonder-blocks-clickable/src/components/clickable.tsx index a61a88731e..324c3fb4b3 100644 --- a/packages/wonder-blocks-clickable/src/components/clickable.tsx +++ b/packages/wonder-blocks-clickable/src/components/clickable.tsx @@ -169,7 +169,7 @@ type Props = target?: never; }); -const StyledAnchor = addStyle("a"); +const StyledA = addStyle("a"); const StyledButton = addStyle("button"); const StyledLink = addStyle(Link); @@ -241,7 +241,7 @@ const Clickable = React.forwardRef(function Clickable( ); } else if (activeHref && !useClient) { return ( - } > {props.children(clickableState)} - + ); } else { return ( diff --git a/packages/wonder-blocks-core/src/util/add-styles.typestest.tsx b/packages/wonder-blocks-core/src/util/add-styles.typestest.tsx index c332eb857e..a66d715e94 100644 --- a/packages/wonder-blocks-core/src/util/add-styles.typestest.tsx +++ b/packages/wonder-blocks-core/src/util/add-styles.typestest.tsx @@ -12,10 +12,10 @@ const styles = StyleSheet.create({ }, }); -const StyledList = addStyle("ul", styles.list); +const StyledUl = addStyle("ul", styles.list); // eslint-disable-next-line @typescript-eslint/no-unused-vars -const list1 = ; +const list1 = ; // eslint-disable-next-line @typescript-eslint/no-unused-vars -const list2 = ; +const list2 = ; diff --git a/packages/wonder-blocks-dropdown/src/components/option-item.tsx b/packages/wonder-blocks-dropdown/src/components/option-item.tsx index 0d60e5d97b..6037d34845 100644 --- a/packages/wonder-blocks-dropdown/src/components/option-item.tsx +++ b/packages/wonder-blocks-dropdown/src/components/option-item.tsx @@ -138,7 +138,7 @@ type DefaultProps = { selected: OptionProps["selected"]; }; -const StyledListItem = addStyle("li"); +const StyledLi = addStyle("li"); /** * For option items that can be selected in a dropdown, selection denoted either @@ -272,7 +272,7 @@ export default class OptionItem extends React.Component { // Only used for Combobox component, not SingleSelect/MultiSelect if (parentComponent === "listbox") { return ( - { // Prevents the combobox from losing focus when clicking // on the option item. @@ -292,7 +292,7 @@ export default class OptionItem extends React.Component { tabIndex={-1} > {this.renderCell()} - + ); } diff --git a/packages/wonder-blocks-form/src/components/text-area.tsx b/packages/wonder-blocks-form/src/components/text-area.tsx index 858ec940d3..220b4dba77 100644 --- a/packages/wonder-blocks-form/src/components/text-area.tsx +++ b/packages/wonder-blocks-form/src/components/text-area.tsx @@ -185,7 +185,7 @@ type TextAreaProps = AriaProps & { resizeType?: "horizontal" | "vertical" | "both" | "none"; }; -const StyledTextArea = addStyle("textarea"); +const StyledTextarea = addStyle("textarea"); const TextArea = React.forwardRef( function TextArea( @@ -259,7 +259,7 @@ const TextArea = React.forwardRef( return ( - unknown; }; -const StyledAnchor = addStyle("a"); +const StyledA = addStyle("a"); const StyledButton = addStyle("button"); const StyledLink = addStyle(Link); @@ -144,13 +144,13 @@ const IconButtonCore: React.ForwardRefExoticComponent< {child} ) : ( - } > {child} - + ); } else { return ( diff --git a/packages/wonder-blocks-icon/src/components/phosphor-icon.tsx b/packages/wonder-blocks-icon/src/components/phosphor-icon.tsx index 4551da7bfe..958803452f 100644 --- a/packages/wonder-blocks-icon/src/components/phosphor-icon.tsx +++ b/packages/wonder-blocks-icon/src/components/phosphor-icon.tsx @@ -8,7 +8,7 @@ import {IconSize, PhosphorIconAsset} from "../types"; // We use a span instead of an img because we want to use the mask-image CSS // property. -const StyledIcon = addStyle("span"); +const StyledSpan = addStyle("span"); type Props = Pick & { /** @@ -99,7 +99,7 @@ export const PhosphorIcon = React.forwardRef(function PhosphorIcon( const iconStyles = _generateStyles(color, pixelSize); return ( - ) : ( - } > {linkContent} - + ); }; diff --git a/packages/wonder-blocks-popover/src/components/popover-content.tsx b/packages/wonder-blocks-popover/src/components/popover-content.tsx index cd57825fed..df8452f979 100644 --- a/packages/wonder-blocks-popover/src/components/popover-content.tsx +++ b/packages/wonder-blocks-popover/src/components/popover-content.tsx @@ -92,7 +92,7 @@ type DefaultProps = { }; // Created to add custom styles to the icon or image elements -const StyledImage = addStyle("img"); +const StyledImg = addStyle("img"); /** * This is the container that is consumed by all the predefined variations. Its @@ -177,7 +177,7 @@ export default class PopoverContent extends React.Component { {typeof icon !== "string" ? ( icon ) : ( - + )} ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5327ca9840..57333efc97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,8 +88,8 @@ importers: specifier: ^29.7.0 version: 29.7.0 '@khanacademy/eslint-config': - specifier: ^5.0.1 - version: 5.0.1(@khanacademy/eslint-plugin@3.1.1(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/eslint-plugin@8.17.0(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint-config-prettier@10.0.1(eslint@8.57.1))(eslint-import-resolver-typescript@3.7.0)(eslint-plugin-import@2.31.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1))(eslint-plugin-prettier@5.2.3(@types/eslint@9.6.1)(eslint-config-prettier@10.0.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.4.2))(eslint-plugin-react@7.37.4(eslint@8.57.1))(eslint@8.57.1) + specifier: ^5.1.0 + version: 5.1.0(@khanacademy/eslint-plugin@3.1.1(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/eslint-plugin@8.17.0(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint-config-prettier@10.0.1(eslint@8.57.1))(eslint-import-resolver-typescript@3.7.0)(eslint-plugin-import@2.31.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1))(eslint-plugin-prettier@5.2.3(@types/eslint@9.6.1)(eslint-config-prettier@10.0.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.4.2))(eslint-plugin-react@7.37.4(eslint@8.57.1))(eslint@8.57.1) '@khanacademy/eslint-plugin': specifier: ^3.1.1 version: 3.1.1(eslint@8.57.1)(typescript@5.7.3) @@ -2390,8 +2390,8 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@khanacademy/eslint-config@5.0.1': - resolution: {integrity: sha512-2cOthcTwYxXGSfdrXEz98pg297L12kJ5DpCGy5AtaVqY1gGYDh0B9W8farPPmTzwi4tGM4p5aE+ESFxJvaWT/Q==} + '@khanacademy/eslint-config@5.1.0': + resolution: {integrity: sha512-JY5XUjeiOLKRwJQc6ssLsFdcnGspDvo9ZiJml5GdPG3H+56Hka4hbdR6l0YBg94qlTeHbvRhrPDzEPPIf5BdWg==} peerDependencies: '@khanacademy/eslint-plugin': ^3.1.1 '@typescript-eslint/eslint-plugin': 8.17.0 @@ -9150,7 +9150,7 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@khanacademy/eslint-config@5.0.1(@khanacademy/eslint-plugin@3.1.1(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/eslint-plugin@8.17.0(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint-config-prettier@10.0.1(eslint@8.57.1))(eslint-import-resolver-typescript@3.7.0)(eslint-plugin-import@2.31.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1))(eslint-plugin-prettier@5.2.3(@types/eslint@9.6.1)(eslint-config-prettier@10.0.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.4.2))(eslint-plugin-react@7.37.4(eslint@8.57.1))(eslint@8.57.1)': + '@khanacademy/eslint-config@5.1.0(@khanacademy/eslint-plugin@3.1.1(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/eslint-plugin@8.17.0(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3))(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint-config-prettier@10.0.1(eslint@8.57.1))(eslint-import-resolver-typescript@3.7.0)(eslint-plugin-import@2.31.0)(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.1))(eslint-plugin-prettier@5.2.3(@types/eslint@9.6.1)(eslint-config-prettier@10.0.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.4.2))(eslint-plugin-react@7.37.4(eslint@8.57.1))(eslint@8.57.1)': dependencies: '@khanacademy/eslint-plugin': 3.1.1(eslint@8.57.1)(typescript@5.7.3) '@typescript-eslint/eslint-plugin': 8.17.0(@typescript-eslint/parser@8.17.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3)