From 611ff51fa124721a8399827e60a71e746bc908a3 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Wed, 5 Nov 2025 15:35:16 -0800 Subject: [PATCH 01/17] [poc/accessibility-checker-in-editor] Add axe-core functionality to editor panel. Update Issues panel to display Alerts in addition to Warnings. Send axe-core results to Issues panel. --- packages/perseus-editor/package.json | 1 + .../src/__docs__/preview-panel.tsx | 4 +- .../src/components/issue-details.tsx | 13 +- .../src/components/issues-panel.tsx | 27 +++- .../src/iframe-content-renderer.tsx | 18 +++ packages/perseus-editor/src/item-editor.tsx | 49 +++++-- packages/perseus-editor/src/messages.ts | 2 + .../perseus-editor/src/util/a11y-checker.ts | 130 ++++++++++++++++++ pnpm-lock.yaml | 15 +- 9 files changed, 233 insertions(+), 26 deletions(-) create mode 100644 packages/perseus-editor/src/util/a11y-checker.ts diff --git a/packages/perseus-editor/package.json b/packages/perseus-editor/package.json index b6a6b66d549..8df899537cc 100644 --- a/packages/perseus-editor/package.json +++ b/packages/perseus-editor/package.json @@ -43,6 +43,7 @@ "@khanacademy/perseus-linter": "workspace:*", "@khanacademy/perseus-score": "workspace:*", "@khanacademy/perseus-utils": "workspace:*", + "axe-core": "^4.11.0", "katex": "0.11.1", "mafs": "^0.19.0", "tiny-invariant": "catalog:prodDeps" diff --git a/packages/perseus-editor/src/__docs__/preview-panel.tsx b/packages/perseus-editor/src/__docs__/preview-panel.tsx index 0b7af92e9dd..d586b09a37d 100644 --- a/packages/perseus-editor/src/__docs__/preview-panel.tsx +++ b/packages/perseus-editor/src/__docs__/preview-panel.tsx @@ -78,7 +78,9 @@ function PreviewPanel({ /> - {children} + + {children} + )} diff --git a/packages/perseus-editor/src/components/issue-details.tsx b/packages/perseus-editor/src/components/issue-details.tsx index 26b34fbcdd7..5f59e73a092 100644 --- a/packages/perseus-editor/src/components/issue-details.tsx +++ b/packages/perseus-editor/src/components/issue-details.tsx @@ -1,6 +1,6 @@ // WidgetIssueDetails.tsx import {isFeatureOn} from "@khanacademy/perseus-core"; -import {color} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import {LabelLarge, LabelSmall} from "@khanacademy/wonder-blocks-typography"; import * as React from "react"; @@ -19,6 +19,11 @@ const IssueDetails = ({apiOptions, issue}: IssueProps) => { const [expanded, setExpanded] = React.useState(false); const toggleVisibility = () => setExpanded(!expanded); + const accordionColor = + issue.type === "Alert" + ? semanticColor.status.critical.background + : semanticColor.status.warning.background; + // TODO(LEMS-3520): Remove this once the "image-widget-upgrade" feature // flag is has been fully rolled out. Also remove the `apiOptions` prop. const imageUpgradeFF = isFeatureOn({apiOptions}, "image-widget-upgrade"); @@ -28,7 +33,9 @@ const IssueDetails = ({apiOptions, issue}: IssueProps) => { animated={true} expanded={expanded} onToggle={toggleVisibility} - containerStyle={{backgroundColor: color.fadedGold8}} + containerStyle={{ + backgroundColor: accordionColor, + }} panelStyle={{backgroundColor: "white"}} header={ { whiteSpace: "nowrap", }} > - {`Warning: ${issue.id}`} + {`${issue.type}: ${issue.id}`} } > diff --git a/packages/perseus-editor/src/components/issues-panel.tsx b/packages/perseus-editor/src/components/issues-panel.tsx index 4ace3352883..21639544e4a 100644 --- a/packages/perseus-editor/src/components/issues-panel.tsx +++ b/packages/perseus-editor/src/components/issues-panel.tsx @@ -1,8 +1,9 @@ import {View} from "@khanacademy/wonder-blocks-core"; import {PhosphorIcon} from "@khanacademy/wonder-blocks-icon"; -import {color as wbColor} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import iconPass from "@phosphor-icons/core/fill/check-circle-fill.svg"; import iconWarning from "@phosphor-icons/core/fill/warning-fill.svg"; +import iconAlert from "@phosphor-icons/core/fill/warning-octagon-fill.svg"; import * as React from "react"; import {useState} from "react"; @@ -12,6 +13,7 @@ import ToggleableCaret from "./toggleable-caret"; import type {APIOptions} from "@khanacademy/perseus"; export type IssueImpact = "low" | "medium" | "high"; +export type IssueType = "Warning" | "Alert"; export type Issue = { id: string; description: string; @@ -19,6 +21,7 @@ export type Issue = { help: string; impact: IssueImpact; message: string; + type: IssueType; }; type IssuesPanelProps = { @@ -32,12 +35,17 @@ const IssuesPanel = ({apiOptions, issues = []}: IssuesPanelProps) => { const [showPanel, setShowPanel] = useState(false); const hasWarnings = issues.length > 0; + const hasAlerts = issues.some((issue) => issue.type === "Alert"); const issuesCount = `${issues.length} issue${ issues.length === 1 ? "" : "s" }`; - const icon = hasWarnings ? iconWarning : iconPass; - const iconColor = hasWarnings ? wbColor.gold : wbColor.green; + const icon = hasAlerts ? iconAlert : hasWarnings ? iconWarning : iconPass; + const iconColor = hasAlerts + ? semanticColor.status.critical.foreground + : hasWarnings + ? semanticColor.status.warning.foreground + : semanticColor.status.success.foreground; const togglePanel = () => { if (hasWarnings) { @@ -45,6 +53,17 @@ const IssuesPanel = ({apiOptions, issues = []}: IssuesPanelProps) => { } }; + const impactOrder = {high: 3, medium: 2, low: 1}; + const sortedIssues = issues.sort((a, b) => { + if (a.type !== b.type) { + return a.type === "Alert" ? -1 : 1; + } + if (impactOrder[b.impact] !== impactOrder[a.impact]) { + return impactOrder[b.impact] - impactOrder[a.impact]; + } + return a.id.localeCompare(b.id); + }); + return (
@@ -74,7 +93,7 @@ const IssuesPanel = ({apiOptions, issues = []}: IssuesPanelProps) => { {showPanel && (
- {issues.map((issue) => ( + {sortedIssues.map((issue) => ( { frame.style.width = "100%"; frame.style.height = "100%"; frame.src = this.props.url; + // Add axe-core library to the iFrame + frame.onload = () => { + const iframeDoc = + frame.contentDocument || frame.contentWindow?.document; + if (iframeDoc) { + const axeCoreScriptElement = iframeDoc.createElement("script"); + axeCoreScriptElement.src = + "https://unpkg.com/axe-core@4.11.0/axe.js"; + iframeDoc.body.appendChild(axeCoreScriptElement); + } else { + // eslint-disable-next-line no-console + console.warn("Unable to add axe-core to iframe document"); + } + setTimeout(() => { + // @ts-expect-error TS2339: Property 'axe' does not exist on type 'Window' + const iFrameAxe = frame.contentWindow?.axe; + }, 1000); + }; if (this.props.datasetKey) { // If the user has specified a data-* attribute to place on the diff --git a/packages/perseus-editor/src/item-editor.tsx b/packages/perseus-editor/src/item-editor.tsx index e10d9318a50..40433e93556 100644 --- a/packages/perseus-editor/src/item-editor.tsx +++ b/packages/perseus-editor/src/item-editor.tsx @@ -9,6 +9,7 @@ import Editor from "./editor"; import IframeContentRenderer from "./iframe-content-renderer"; import ItemExtrasEditor from "./item-extras-editor"; import {WARNINGS} from "./messages"; +import {runAxeCoreOnUpdate} from "./util/a11y-checker"; import {ItemEditorContext} from "./util/item-editor-context"; import type {Issue} from "./components/issues-panel"; @@ -49,6 +50,7 @@ type Props = { type State = { issues: Issue[]; + axeCoreIssues: Issue[]; }; class ItemEditor extends React.Component { @@ -63,6 +65,7 @@ class ItemEditor extends React.Component { }; static prevContent: string | undefined; static prevWidgets: PerseusWidgetsMap | undefined; + a11yCheckerTimeoutId: any; frame = React.createRef(); questionEditor = React.createRef(); @@ -70,31 +73,31 @@ class ItemEditor extends React.Component { state = { issues: [], + axeCoreIssues: [], }; - static getDerivedStateFromProps(props: Props): Partial | null { + componentDidUpdate(prevProps: Props) { // Short-circuit if nothing changed if ( - props.question?.content === ItemEditor.prevContent && - props.question?.widgets === ItemEditor.prevWidgets + this.props.question?.content === prevProps.question?.content && + this.props.question?.widgets === prevProps.question?.widgets ) { - return null; + return; } - // Update cached values - ItemEditor.prevContent = props.question?.content; - ItemEditor.prevWidgets = props.question?.widgets; - - const parsed = PerseusMarkdown.parse(props.question?.content ?? "", {}); + const parsed = PerseusMarkdown.parse( + this.props.question?.content ?? "", + {}, + ); const linterContext = { - content: props.question?.content, - widgets: props.question?.widgets, + content: this.props.question?.content, + widgets: this.props.question?.widgets, stack: [], }; - return { - issues: [ - ...(props.issues ?? []), + const gatherIssues = () => { + return [ + ...(this.props.issues ?? []), ...(PerseusLinter.runLinter(parsed, linterContext, false)?.map( (linterWarning) => { if (linterWarning.rule === "inaccessible-widget") { @@ -109,8 +112,24 @@ class ItemEditor extends React.Component { ); }, ) ?? []), - ], + ...this.state.axeCoreIssues, + ]; }; + + this.a11yCheckerTimeoutId = runAxeCoreOnUpdate( + this.a11yCheckerTimeoutId, + (issues) => { + const allIssues = [...this.state.issues, ...issues]; + this.setState({ + issues: allIssues, + axeCoreIssues: issues, + }); + }, + ); + + this.setState({ + issues: gatherIssues(), + }); } // Notify the parent that the question or answer area has been updated. diff --git a/packages/perseus-editor/src/messages.ts b/packages/perseus-editor/src/messages.ts index 91ddcec3e5d..beaf1e4c47c 100644 --- a/packages/perseus-editor/src/messages.ts +++ b/packages/perseus-editor/src/messages.ts @@ -11,6 +11,7 @@ export const WARNINGS = { impact: "medium", message: "Selecting inaccessible widgets for a practice item will result in this exercise being hidden from users with 'Hide visually dependant content' setting set to true. Please select another widget or create an alternative practice item.", + type: "Warning", }), genericLinterWarning: (rule: string, message: string): Issue => ({ @@ -21,5 +22,6 @@ export const WARNINGS = { "https://docs.google.com/document/d/1N13f4sY-7EXWDwQ04ivA9vJBVvPPd60qjBT73B4NHuM/edit?tab=t.0", impact: "low", message: message, + type: "Warning", }), }; diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts new file mode 100644 index 00000000000..1f43ee39b7c --- /dev/null +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -0,0 +1,130 @@ +import * as axeCore from "axe-core"; + +import type {Issue, IssueType} from "../components/issues-panel"; +import type axe from "axe-core"; + +const axeCoreEditorOptions = { + include: { + fromFrames: ["iframe", "#page-container"], + }, + exclude: { + fromFrames: ["iframe", '[target="lint-help-window"]'], + }, +}; + +const axeCoreStorybookOptions = { + include: ["#preview-panel"], + exclude: ['[target="lint-help-window"]'], +}; + +const convertAxeImpactToIssueImpact = ( + impact?: axe.ImpactValue, +): Issue["impact"] => { + switch (impact) { + case "critical": + return "high"; + case "serious": + return "high"; + case "moderate": + return "medium"; + case "minor": + return "low"; + default: + return "low"; + } +}; + +const getIssueMessage = (nodes: axe.NodeResult[]): string => { + return nodes + .map((node) => { + return node.all + .concat(node.any, node.none) + .map((result) => result.message) + .join(" "); + }) + .join(" "); +}; + +const getIssueElements = (nodes: axe.NodeResult[]): Element[] => { + const nodeToCheck = nodes.length > 0 ? [nodes[0]] : []; + // @ts-expect-error TS2322: Type 'string[]' is not assignable to type 'Element[]'. + return nodeToCheck.flatMap((node) => { + // @ts-expect-error TS2769: No overload matches this call. + return node.target.reduce((elements: Element[], target: string) => { + // eslint-disable-next-line no-console + console.log(` Issue Target: `, target); + let element: Element | null; + if ( + elements.length > 0 && + elements[elements.length - 1].tagName.toLowerCase() === "iframe" + ) { + // eslint-disable-next-line no-console + console.log(` Prior target is iFrame`); + element = + // @ts-expect-error TS2551: Property 'contentDocument' does not exist on type 'Element' + elements[elements.length - 1].contentDocument.querySelector( + target, + ); + } else { + // eslint-disable-next-line no-console + console.log(` Prior target is NOT iFrame`); + element = document.querySelector(target); + } + // eslint-disable-next-line no-console + console.log(` Element: `, element); + if (element) { + elements.push(element); + } + return elements; + }, []); + }); +}; + +const mapResultsToIssues = ( + results: axe.Result[], + type: IssueType, +): Issue[] => { + return results.map((result) => { + return { + id: result.id, + description: "", //result.description, + helpUrl: result.helpUrl, + help: result.help, + impact: convertAxeImpactToIssueImpact(result.impact), + message: getIssueMessage(result.nodes), + type: type, + }; + }); +}; + +const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void) => { + const isInStorybook = !!document.getElementById("storybook-root"); + const options = isInStorybook + ? axeCoreStorybookOptions + : axeCoreEditorOptions; + axeCore.configure({reporter: "v2"}); + // @ts-expect-error TS2769: No overload matches this call. + axeCore.run(options).then( + (results) => { + // eslint-disable-next-line no-console + console.log(`Accessibility Results: `, results); + const violations = mapResultsToIssues(results.violations, "Alert"); + const incompletes = mapResultsToIssues( + results.incomplete, + "Warning", + ); + const issues = violations.concat(incompletes); + console.log(` Issues: `, issues); + updateIssuesFn(issues); + }, + (error) => { + // eslint-disable-next-line no-console + console.log(` Error: `, error); + }, + ); +}; + +export const runAxeCoreOnUpdate = (priorTimeoutId, setState): any => { + clearTimeout(priorTimeoutId); + return setTimeout(runAxeCore, 1500, setState); +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 67c8c1e3c81..207f09f44f1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -827,6 +827,9 @@ importers: '@khanacademy/perseus-utils': specifier: workspace:* version: link:../perseus-utils + axe-core: + specifier: ^4.11.0 + version: 4.11.0 katex: specifier: 0.11.1 version: 0.11.1 @@ -3789,6 +3792,10 @@ packages: resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} engines: {node: '>=4'} + axe-core@4.11.0: + resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} + engines: {node: '>=4'} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -12766,6 +12773,8 @@ snapshots: axe-core@4.10.2: {} + axe-core@4.11.0: {} + axobject-query@4.1.0: {} babel-jest@29.7.0(@babel/core@7.26.0): @@ -17529,7 +17538,7 @@ snapshots: react-router-dom@5.3.4(react@18.2.0): dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.27.1 history: 4.10.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -17540,7 +17549,7 @@ snapshots: react-router@5.3.4(react@18.2.0): dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.27.1 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -17576,7 +17585,7 @@ snapshots: react-window@1.8.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.27.1 memoize-one: 5.2.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) From ed6ceab5f250e462a996773a0349925bddf86da3 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Wed, 5 Nov 2025 17:17:22 -0800 Subject: [PATCH 02/17] [poc/accessibility-checker-in-editor] Add "Show me" button to highlight element with the issue. --- .../src/components/issue-details.tsx | 2 + .../src/components/issues-panel.tsx | 1 + .../src/components/show-me-issue.tsx | 77 +++++++++++++++++++ packages/perseus-editor/src/item-editor.tsx | 34 ++++---- .../src/styles/perseus-editor.css | 2 + .../perseus-editor/src/util/a11y-checker.ts | 3 +- 6 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 packages/perseus-editor/src/components/show-me-issue.tsx diff --git a/packages/perseus-editor/src/components/issue-details.tsx b/packages/perseus-editor/src/components/issue-details.tsx index 5f59e73a092..9d936e650a5 100644 --- a/packages/perseus-editor/src/components/issue-details.tsx +++ b/packages/perseus-editor/src/components/issue-details.tsx @@ -6,6 +6,7 @@ import * as React from "react"; import IssueCta from "./issue-cta"; import PerseusEditorAccordion from "./perseus-editor-accordion"; +import ShowMe from "./show-me-issue"; import type {Issue} from "./issues-panel"; import type {APIOptions} from "@khanacademy/perseus"; @@ -63,6 +64,7 @@ const IssueDetails = ({apiOptions, issue}: IssueProps) => { Issue: {issue.message} + {imageUpgradeFF && } ); diff --git a/packages/perseus-editor/src/components/issues-panel.tsx b/packages/perseus-editor/src/components/issues-panel.tsx index 21639544e4a..9a811377280 100644 --- a/packages/perseus-editor/src/components/issues-panel.tsx +++ b/packages/perseus-editor/src/components/issues-panel.tsx @@ -17,6 +17,7 @@ export type IssueType = "Warning" | "Alert"; export type Issue = { id: string; description: string; + elements?: Element[]; helpUrl: string; help: string; impact: IssueImpact; diff --git a/packages/perseus-editor/src/components/show-me-issue.tsx b/packages/perseus-editor/src/components/show-me-issue.tsx new file mode 100644 index 00000000000..5d4e6cfd08b --- /dev/null +++ b/packages/perseus-editor/src/components/show-me-issue.tsx @@ -0,0 +1,77 @@ +import Switch from "@khanacademy/wonder-blocks-switch"; +import {LabelSmall} from "@khanacademy/wonder-blocks-typography"; +import * as React from "react"; +import {useState} from "react"; + +import type {CSSProperties} from "react"; + +const ShowMe = ({elements}: {elements?: Element[]}) => { + const [showMe, setShowMe] = useState(false); + + if (!elements || elements.length === 0) { + return null; + } + // eslint-disable-next-line no-console + console.log(` Issue Elements: `, elements); + const issueBoundary = elements.reduce( + (boundary: DOMRect, element: Element, index: number) => { + const elementBoundary = element.getBoundingClientRect(); + // eslint-disable-next-line no-console + console.log(` Initial Boundary: `, boundary); + // eslint-disable-next-line no-console + console.log(` Element: `, element); + // eslint-disable-next-line no-console + console.log(` Element Boundary: `, elementBoundary); + boundary.top += elementBoundary.top; + boundary.left += elementBoundary.left; + if (index === elements.length - 1) { + boundary.height = elementBoundary.height; + boundary.width = elementBoundary.width; + } + // eslint-disable-next-line no-console + console.log(` Adjusted Boundary: `, boundary); + return boundary; + }, + {top: 0, left: 0, height: 0, width: 0}, + ); + // eslint-disable-next-line no-console + console.log(` Final Boundary: `, issueBoundary); + const showMeStyle = { + marginTop: "1em", + fontWeight: "bold", + display: "flex", + alignItems: "center", + }; + const showMeOutlineStyle: CSSProperties = + showMe && issueBoundary.width !== 0 + ? { + display: "block", + border: "2px solid red", + borderRadius: "4px", + position: "fixed", + height: issueBoundary.height + 8, + width: issueBoundary.width + 8, + top: issueBoundary.top - 4, + left: issueBoundary.left - 4, + } + : {display: "none"}; + + const showMeToggle = ( + + Show Me + +
+ + ); + const showMeUnavailable = ( +
+ Unable to find the offending element. Please ask a developer for + help fixing this. +
+ ); + + // eslint-disable-next-line + return issueBoundary ? showMeToggle : showMeUnavailable; +}; + +export default ShowMe; diff --git a/packages/perseus-editor/src/item-editor.tsx b/packages/perseus-editor/src/item-editor.tsx index 40433e93556..a82bdc2cb70 100644 --- a/packages/perseus-editor/src/item-editor.tsx +++ b/packages/perseus-editor/src/item-editor.tsx @@ -95,6 +95,15 @@ class ItemEditor extends React.Component { stack: [], }; + this.a11yCheckerTimeoutId = runAxeCoreOnUpdate( + this.a11yCheckerTimeoutId, + (issues) => { + this.setState({ + axeCoreIssues: issues, + }); + }, + ); + const gatherIssues = () => { return [ ...(this.props.issues ?? []), @@ -112,21 +121,9 @@ class ItemEditor extends React.Component { ); }, ) ?? []), - ...this.state.axeCoreIssues, ]; }; - this.a11yCheckerTimeoutId = runAxeCoreOnUpdate( - this.a11yCheckerTimeoutId, - (issues) => { - const allIssues = [...this.state.issues, ...issues]; - this.setState({ - issues: allIssues, - axeCoreIssues: issues, - }); - }, - ); - this.setState({ issues: gatherIssues(), }); @@ -170,10 +167,11 @@ class ItemEditor extends React.Component { }; render(): React.ReactNode { - const isMobile = - this.props.deviceType === "phone" || - this.props.deviceType === "tablet"; + // const isMobile = + // this.props.deviceType === "phone" || + // this.props.deviceType === "tablet"; const editingDisabled = this.props.apiOptions?.editingDisabled ?? false; + const allIssues = this.state.issues.concat(this.state.axeCoreIssues); return ( {
Question
@@ -212,7 +210,7 @@ class ItemEditor extends React.Component { />
- +{/*
{ />
+*/}
-
Question extras
diff --git a/packages/perseus-editor/src/styles/perseus-editor.css b/packages/perseus-editor/src/styles/perseus-editor.css index 934c82536a6..d2a13baeb74 100644 --- a/packages/perseus-editor/src/styles/perseus-editor.css +++ b/packages/perseus-editor/src/styles/perseus-editor.css @@ -233,6 +233,8 @@ code { } #perseus { margin: 20px; + width: calc(100% - 500px); + z-index: 10; } #perseus #problemarea #workarea { margin: 0; diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index 1f43ee39b7c..210efaa2fe7 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -88,6 +88,7 @@ const mapResultsToIssues = ( return { id: result.id, description: "", //result.description, + elements: getIssueElements(result.nodes), helpUrl: result.helpUrl, help: result.help, impact: convertAxeImpactToIssueImpact(result.impact), @@ -97,7 +98,7 @@ const mapResultsToIssues = ( }); }; -const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void) => { +const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { const isInStorybook = !!document.getElementById("storybook-root"); const options = isInStorybook ? axeCoreStorybookOptions From 08fca80fdf43bbc9d1365dcec722eec3c287c3ba Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 14:38:22 -0800 Subject: [PATCH 03/17] [poc/accessibility-checker-in-editor] Add issues list that indicates which issues are user-fixable so that content creators know that they should send us the info. Update color tokens to use the correct tokens (status is being deprecated). --- .../src/components/issue-details.tsx | 4 ++-- .../src/components/issues-panel.tsx | 6 +++--- .../perseus-editor/src/util/a11y-checker.ts | 16 ++++++++++++++-- .../src/util/a11y-issues-list.json | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 packages/perseus-editor/src/util/a11y-issues-list.json diff --git a/packages/perseus-editor/src/components/issue-details.tsx b/packages/perseus-editor/src/components/issue-details.tsx index 9d936e650a5..e557c729c3b 100644 --- a/packages/perseus-editor/src/components/issue-details.tsx +++ b/packages/perseus-editor/src/components/issue-details.tsx @@ -22,8 +22,8 @@ const IssueDetails = ({apiOptions, issue}: IssueProps) => { const accordionColor = issue.type === "Alert" - ? semanticColor.status.critical.background - : semanticColor.status.warning.background; + ? semanticColor.feedback.critical.subtle.background + : semanticColor.feedback.warning.subtle.background; // TODO(LEMS-3520): Remove this once the "image-widget-upgrade" feature // flag is has been fully rolled out. Also remove the `apiOptions` prop. diff --git a/packages/perseus-editor/src/components/issues-panel.tsx b/packages/perseus-editor/src/components/issues-panel.tsx index 9a811377280..62396ed3a67 100644 --- a/packages/perseus-editor/src/components/issues-panel.tsx +++ b/packages/perseus-editor/src/components/issues-panel.tsx @@ -43,10 +43,10 @@ const IssuesPanel = ({apiOptions, issues = []}: IssuesPanelProps) => { const icon = hasAlerts ? iconAlert : hasWarnings ? iconWarning : iconPass; const iconColor = hasAlerts - ? semanticColor.status.critical.foreground + ? semanticColor.feedback.critical.strong.icon : hasWarnings - ? semanticColor.status.warning.foreground - : semanticColor.status.success.foreground; + ? semanticColor.feedback.warning.strong.icon + : semanticColor.feedback.success.strong.icon; const togglePanel = () => { if (hasWarnings) { diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index 210efaa2fe7..b3a239cbff4 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -1,8 +1,13 @@ import * as axeCore from "axe-core"; +import issuesList from "./a11y-issues-list.json"; + import type {Issue, IssueType} from "../components/issues-panel"; import type axe from "axe-core"; +const assistanceNeededMessage = + "Developer assistance needed - Please send this exercise and warning info to the LEMS team for review."; + const axeCoreEditorOptions = { include: { fromFrames: ["iframe", "#page-container"], @@ -11,7 +16,6 @@ const axeCoreEditorOptions = { fromFrames: ["iframe", '[target="lint-help-window"]'], }, }; - const axeCoreStorybookOptions = { include: ["#preview-panel"], exclude: ['[target="lint-help-window"]'], @@ -84,10 +88,18 @@ const mapResultsToIssues = ( results: axe.Result[], type: IssueType, ): Issue[] => { + console.log("Issues List: ", issuesList); + console.log("User Fixable Issues: ", issuesList["user-fixable"]); return results.map((result) => { + const isUserFixable = + type === "Alert" && + issuesList["axe-core"]["user-fixable"].some( + (testId) => testId === result.id, + ); + const description = isUserFixable ? "" : assistanceNeededMessage; return { id: result.id, - description: "", //result.description, + description: description, elements: getIssueElements(result.nodes), helpUrl: result.helpUrl, help: result.help, diff --git a/packages/perseus-editor/src/util/a11y-issues-list.json b/packages/perseus-editor/src/util/a11y-issues-list.json new file mode 100644 index 00000000000..9f93bc52c5f --- /dev/null +++ b/packages/perseus-editor/src/util/a11y-issues-list.json @@ -0,0 +1,19 @@ +{ + "axe-core": { + "user-fixable": [ + "aria-input-field-name", + "aria-meter-name", + "aria-toggle-field-name", + "image-alt", + "input-image-alt", + "label", + "link-name", + "list", + "role-img-alt", + "select-name", + "summary-name", + "svg-img-alt", + "video-caption" + ] + } +} From a945cfaebea0f3dc20d6442a6810c47fbd11c156 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 14:49:01 -0800 Subject: [PATCH 04/17] [poc/accessibility-checker-in-editor] Change issues list to a JS file instead of JSON. --- packages/perseus-editor/src/util/a11y-checker.ts | 2 +- .../{a11y-issues-list.json => a11y-issues-list.ts} | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) rename packages/perseus-editor/src/util/{a11y-issues-list.json => a11y-issues-list.ts} (85%) diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index b3a239cbff4..89900e2880f 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -1,6 +1,6 @@ import * as axeCore from "axe-core"; -import issuesList from "./a11y-issues-list.json"; +import issuesList from "./a11y-issues-list"; import type {Issue, IssueType} from "../components/issues-panel"; import type axe from "axe-core"; diff --git a/packages/perseus-editor/src/util/a11y-issues-list.json b/packages/perseus-editor/src/util/a11y-issues-list.ts similarity index 85% rename from packages/perseus-editor/src/util/a11y-issues-list.json rename to packages/perseus-editor/src/util/a11y-issues-list.ts index 9f93bc52c5f..54f4d82b8de 100644 --- a/packages/perseus-editor/src/util/a11y-issues-list.json +++ b/packages/perseus-editor/src/util/a11y-issues-list.ts @@ -1,4 +1,4 @@ -{ +export default { "axe-core": { "user-fixable": [ "aria-input-field-name", @@ -13,7 +13,7 @@ "select-name", "summary-name", "svg-img-alt", - "video-caption" - ] - } -} + "video-caption", + ], + }, +}; From c04451853f83d2d00f6d5c63182fc74cf264fcd9 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 15:01:12 -0800 Subject: [PATCH 05/17] [poc/accessibility-checker-in-editor] Fix TSC error. --- .../perseus-editor/src/components/show-me-issue.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/perseus-editor/src/components/show-me-issue.tsx b/packages/perseus-editor/src/components/show-me-issue.tsx index 5d4e6cfd08b..4e67d5eba60 100644 --- a/packages/perseus-editor/src/components/show-me-issue.tsx +++ b/packages/perseus-editor/src/components/show-me-issue.tsx @@ -5,6 +5,13 @@ import {useState} from "react"; import type {CSSProperties} from "react"; +type BoundaryRect = { + top: number; + left: number; + height: number; + width: number; +}; + const ShowMe = ({elements}: {elements?: Element[]}) => { const [showMe, setShowMe] = useState(false); @@ -13,8 +20,8 @@ const ShowMe = ({elements}: {elements?: Element[]}) => { } // eslint-disable-next-line no-console console.log(` Issue Elements: `, elements); - const issueBoundary = elements.reduce( - (boundary: DOMRect, element: Element, index: number) => { + const issueBoundary = elements?.reduce( + (boundary: BoundaryRect, element: Element, index: number) => { const elementBoundary = element.getBoundingClientRect(); // eslint-disable-next-line no-console console.log(` Initial Boundary: `, boundary); From fca43cb11466292c2da6f4739f9df948ac4a60aa Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 15:30:36 -0800 Subject: [PATCH 06/17] [poc/accessibility-checker-in-editor] Restore problemarea for exercise preview. --- packages/perseus-editor/src/item-editor.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/perseus-editor/src/item-editor.tsx b/packages/perseus-editor/src/item-editor.tsx index a82bdc2cb70..77f5c9ad88b 100644 --- a/packages/perseus-editor/src/item-editor.tsx +++ b/packages/perseus-editor/src/item-editor.tsx @@ -210,7 +210,6 @@ class ItemEditor extends React.Component { />
-{/*
{ />
-*/}
From f72d46de4595ece8934ed826790df622b807b6e9 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 15:31:14 -0800 Subject: [PATCH 07/17] [poc/accessibility-checker-in-editor] Restore isMobile definition. --- packages/perseus-editor/src/item-editor.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/perseus-editor/src/item-editor.tsx b/packages/perseus-editor/src/item-editor.tsx index 77f5c9ad88b..e55314d1dde 100644 --- a/packages/perseus-editor/src/item-editor.tsx +++ b/packages/perseus-editor/src/item-editor.tsx @@ -167,9 +167,9 @@ class ItemEditor extends React.Component { }; render(): React.ReactNode { - // const isMobile = - // this.props.deviceType === "phone" || - // this.props.deviceType === "tablet"; + const isMobile = + this.props.deviceType === "phone" || + this.props.deviceType === "tablet"; const editingDisabled = this.props.apiOptions?.editingDisabled ?? false; const allIssues = this.state.issues.concat(this.state.axeCoreIssues); From 2360acf141986d9f4286d63a718a4cb72762ac89 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 15:37:25 -0800 Subject: [PATCH 08/17] [poc/accessibility-checker-in-editor] Hide problemarea when in Storybook page. --- packages/perseus-editor/src/styles/perseus-editor.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/perseus-editor/src/styles/perseus-editor.css b/packages/perseus-editor/src/styles/perseus-editor.css index d2a13baeb74..8ed3937f7ab 100644 --- a/packages/perseus-editor/src/styles/perseus-editor.css +++ b/packages/perseus-editor/src/styles/perseus-editor.css @@ -259,6 +259,9 @@ code { float: left; padding-bottom: 38px; } +#storybook-root #problemarea { + display: none; +} #problemarea a:link, #problemarea input, #problemarea label, From b7c9349169d38b71b0740335d7c467669d0ca7a9 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 16:32:01 -0800 Subject: [PATCH 09/17] [poc/accessibility-checker-in-editor] Adjust CSS to ensure that the left cell of the editor is stacked above the right cell so that the Show Me outline is visible. Adjust axe-core configuration to only scan the preview iFrame that is visible when editing, not the one that is in the "Preview" tab (both are rendered in the page). --- packages/perseus-editor/src/styles/perseus-editor.css | 2 ++ packages/perseus-editor/src/util/a11y-checker.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/perseus-editor/src/styles/perseus-editor.css b/packages/perseus-editor/src/styles/perseus-editor.css index 8ed3937f7ab..bb64a97ac88 100644 --- a/packages/perseus-editor/src/styles/perseus-editor.css +++ b/packages/perseus-editor/src/styles/perseus-editor.css @@ -286,6 +286,8 @@ code { width: 360px; max-width: 360px; min-width: 360px; + position: relative; + z-index: 10; } .perseus-editor-right-cell { box-sizing: border-box; diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index 89900e2880f..ec7cfb8d673 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -10,7 +10,7 @@ const assistanceNeededMessage = const axeCoreEditorOptions = { include: { - fromFrames: ["iframe", "#page-container"], + fromFrames: ['iframe[src^="/perseus/frame"]', "#page-container"], }, exclude: { fromFrames: ["iframe", '[target="lint-help-window"]'], From e6e5311df0c95735001a129f652f13cfb38e60ca Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 16:54:12 -0800 Subject: [PATCH 10/17] [poc/accessibility-checker-in-editor] Adjust include/exclude configuration. --- packages/perseus-editor/src/util/a11y-checker.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index ec7cfb8d673..0bdd2c39a85 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -13,7 +13,10 @@ const axeCoreEditorOptions = { fromFrames: ['iframe[src^="/perseus/frame"]', "#page-container"], }, exclude: { - fromFrames: ["iframe", '[target="lint-help-window"]'], + fromFrames: [ + 'iframe[src^="/perseus/frame"]', + '[target="lint-help-window"]', + ], }, }; const axeCoreStorybookOptions = { From d68babffac06c5896b52f524de537597b01275b9 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Thu, 6 Nov 2025 17:30:43 -0800 Subject: [PATCH 11/17] [poc/accessibility-checker-in-editor] Add logic to wait until preview frame has rendered before running axe-core. --- .../perseus-editor/src/iframe-content-renderer.tsx | 8 ++++---- packages/perseus-editor/src/util/a11y-checker.ts | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/perseus-editor/src/iframe-content-renderer.tsx b/packages/perseus-editor/src/iframe-content-renderer.tsx index 683e634fc04..9902192840f 100644 --- a/packages/perseus-editor/src/iframe-content-renderer.tsx +++ b/packages/perseus-editor/src/iframe-content-renderer.tsx @@ -153,10 +153,10 @@ class IframeContentRenderer extends React.Component { // eslint-disable-next-line no-console console.warn("Unable to add axe-core to iframe document"); } - setTimeout(() => { - // @ts-expect-error TS2339: Property 'axe' does not exist on type 'Window' - const iFrameAxe = frame.contentWindow?.axe; - }, 1000); + // setTimeout(() => { + // // @ts-expect-error TS2339: Property 'axe' does not exist on type 'Window' + // const iFrameAxe = frame.contentWindow?.axe; + // }, 1000); }; if (this.props.datasetKey) { diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index 0bdd2c39a85..7844a08d59f 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -115,6 +115,20 @@ const mapResultsToIssues = ( const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { const isInStorybook = !!document.getElementById("storybook-root"); + if (!isInStorybook) { + let frameHasLoaded = false; + const frame = document.querySelector('iframe[src^="/perseus/frame"]'); + if (frame) { + const frameDocument = + // @ts-expect-error TS2551: Property 'contentDocument' does not exist on type 'Element'. + frame.contentDocument || frame.contentWindow?.document; + frameHasLoaded = frameDocument?.readyState === "complete"; + } + if (!frameHasLoaded) { + setTimeout(runAxeCore, 100); + return; + } + } const options = isInStorybook ? axeCoreStorybookOptions : axeCoreEditorOptions; From ce025acdba9471e65dabfbbbbf39965f6b78df64 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Fri, 7 Nov 2025 14:01:10 -0800 Subject: [PATCH 12/17] [poc/accessibility-checker-in-editor] Adjust delay when waiting for iframe to load. --- packages/perseus-editor/src/util/a11y-checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index 7844a08d59f..18e88956e65 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -125,7 +125,7 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { frameHasLoaded = frameDocument?.readyState === "complete"; } if (!frameHasLoaded) { - setTimeout(runAxeCore, 100); + setTimeout(runAxeCore, 1000); return; } } From 635f4772c68f72e0e0a76f640222957cbecabfe1 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Fri, 7 Nov 2025 14:22:09 -0800 Subject: [PATCH 13/17] [poc/accessibility-checker-in-editor] Add logic to handle iFrames that still have to render, even though they have been "loaded" from an event listener standpoint. --- packages/perseus-editor/src/util/a11y-checker.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index 18e88956e65..370e2d67c2b 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -125,7 +125,7 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { frameHasLoaded = frameDocument?.readyState === "complete"; } if (!frameHasLoaded) { - setTimeout(runAxeCore, 1000); + setTimeout(runAxeCore, 100); return; } } @@ -145,7 +145,15 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { ); const issues = violations.concat(incompletes); console.log(` Issues: `, issues); - updateIssuesFn(issues); + if ( + violations.length === 0 && + incompletes.length === 0 && + results.passes.length === 0 + ) { + setTimeout(runAxeCore, 1000); // No valid results indicates that content may not be fully loaded yet + } else { + updateIssuesFn(issues); + } }, (error) => { // eslint-disable-next-line no-console From 4f8e87bbcb4dc29d866cc6a2ddba1027f1907dfb Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Fri, 7 Nov 2025 14:37:40 -0800 Subject: [PATCH 14/17] [poc/accessibility-checker-in-editor] Add debugging messages. --- packages/perseus-editor/src/util/a11y-checker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index 370e2d67c2b..e9bc8753f72 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -125,6 +125,7 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { frameHasLoaded = frameDocument?.readyState === "complete"; } if (!frameHasLoaded) { + console.log("Frame has NOT loaded"); setTimeout(runAxeCore, 100); return; } @@ -150,6 +151,7 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { incompletes.length === 0 && results.passes.length === 0 ) { + console.log("No valid results - Retrying..."); setTimeout(runAxeCore, 1000); // No valid results indicates that content may not be fully loaded yet } else { updateIssuesFn(issues); From 32d5dce87c2a93e17202a5155cec2cae073dd788 Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Fri, 7 Nov 2025 14:45:30 -0800 Subject: [PATCH 15/17] [poc/accessibility-checker-in-editor] Add update function for retry callback. --- packages/perseus-editor/src/util/a11y-checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index e9bc8753f72..a7d33bf53d6 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -152,7 +152,7 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { results.passes.length === 0 ) { console.log("No valid results - Retrying..."); - setTimeout(runAxeCore, 1000); // No valid results indicates that content may not be fully loaded yet + setTimeout(runAxeCore, 1000, updateIssuesFn); // No valid results indicates that content may not be fully loaded yet } else { updateIssuesFn(issues); } From 83fd8a63262d43dd3676e3cc37da61637311ebcf Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Fri, 7 Nov 2025 15:07:01 -0800 Subject: [PATCH 16/17] [poc/accessibility-checker-in-editor] Add update function to callback, and adjust timing for post-loaded retries. --- packages/perseus-editor/src/util/a11y-checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index a7d33bf53d6..0d0420d39b8 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -126,7 +126,7 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { } if (!frameHasLoaded) { console.log("Frame has NOT loaded"); - setTimeout(runAxeCore, 100); + setTimeout(runAxeCore, 100, updateIssuesFn); return; } } @@ -152,7 +152,7 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { results.passes.length === 0 ) { console.log("No valid results - Retrying..."); - setTimeout(runAxeCore, 1000, updateIssuesFn); // No valid results indicates that content may not be fully loaded yet + setTimeout(runAxeCore, 1500, updateIssuesFn); // No valid results indicates that content may not be fully loaded yet } else { updateIssuesFn(issues); } From b5ac29d50e373db9d8026e6cfecde61cb7175d9b Mon Sep 17 00:00:00 2001 From: Mark Fitzgerald Date: Fri, 7 Nov 2025 16:31:56 -0800 Subject: [PATCH 17/17] [poc/accessibility-checker-in-editor] Remove console messages. --- .../src/__tests__/issues-panel.test.tsx | 1 + .../perseus-editor/src/components/show-me-issue.tsx | 12 ------------ .../perseus-editor/src/iframe-content-renderer.tsx | 4 ---- packages/perseus-editor/src/util/a11y-checker.ts | 13 +------------ 4 files changed, 2 insertions(+), 28 deletions(-) diff --git a/packages/perseus-editor/src/__tests__/issues-panel.test.tsx b/packages/perseus-editor/src/__tests__/issues-panel.test.tsx index c3d77960f3a..fa8c8140c7e 100644 --- a/packages/perseus-editor/src/__tests__/issues-panel.test.tsx +++ b/packages/perseus-editor/src/__tests__/issues-panel.test.tsx @@ -23,6 +23,7 @@ const makeIssue = (id: string, impact: IssueImpact = "medium") => ({ help: "Example help", impact, message: "Example message", + type: "Warning" as const, }); describe("IssuesPanel", () => { diff --git a/packages/perseus-editor/src/components/show-me-issue.tsx b/packages/perseus-editor/src/components/show-me-issue.tsx index 4e67d5eba60..e51a0ff2c2a 100644 --- a/packages/perseus-editor/src/components/show-me-issue.tsx +++ b/packages/perseus-editor/src/components/show-me-issue.tsx @@ -18,31 +18,19 @@ const ShowMe = ({elements}: {elements?: Element[]}) => { if (!elements || elements.length === 0) { return null; } - // eslint-disable-next-line no-console - console.log(` Issue Elements: `, elements); const issueBoundary = elements?.reduce( (boundary: BoundaryRect, element: Element, index: number) => { const elementBoundary = element.getBoundingClientRect(); - // eslint-disable-next-line no-console - console.log(` Initial Boundary: `, boundary); - // eslint-disable-next-line no-console - console.log(` Element: `, element); - // eslint-disable-next-line no-console - console.log(` Element Boundary: `, elementBoundary); boundary.top += elementBoundary.top; boundary.left += elementBoundary.left; if (index === elements.length - 1) { boundary.height = elementBoundary.height; boundary.width = elementBoundary.width; } - // eslint-disable-next-line no-console - console.log(` Adjusted Boundary: `, boundary); return boundary; }, {top: 0, left: 0, height: 0, width: 0}, ); - // eslint-disable-next-line no-console - console.log(` Final Boundary: `, issueBoundary); const showMeStyle = { marginTop: "1em", fontWeight: "bold", diff --git a/packages/perseus-editor/src/iframe-content-renderer.tsx b/packages/perseus-editor/src/iframe-content-renderer.tsx index 9902192840f..06f9e862e9f 100644 --- a/packages/perseus-editor/src/iframe-content-renderer.tsx +++ b/packages/perseus-editor/src/iframe-content-renderer.tsx @@ -153,10 +153,6 @@ class IframeContentRenderer extends React.Component { // eslint-disable-next-line no-console console.warn("Unable to add axe-core to iframe document"); } - // setTimeout(() => { - // // @ts-expect-error TS2339: Property 'axe' does not exist on type 'Window' - // const iFrameAxe = frame.contentWindow?.axe; - // }, 1000); }; if (this.props.datasetKey) { diff --git a/packages/perseus-editor/src/util/a11y-checker.ts b/packages/perseus-editor/src/util/a11y-checker.ts index 0d0420d39b8..5deec9ea995 100644 --- a/packages/perseus-editor/src/util/a11y-checker.ts +++ b/packages/perseus-editor/src/util/a11y-checker.ts @@ -58,27 +58,19 @@ const getIssueElements = (nodes: axe.NodeResult[]): Element[] => { return nodeToCheck.flatMap((node) => { // @ts-expect-error TS2769: No overload matches this call. return node.target.reduce((elements: Element[], target: string) => { - // eslint-disable-next-line no-console - console.log(` Issue Target: `, target); let element: Element | null; if ( elements.length > 0 && elements[elements.length - 1].tagName.toLowerCase() === "iframe" ) { - // eslint-disable-next-line no-console - console.log(` Prior target is iFrame`); element = // @ts-expect-error TS2551: Property 'contentDocument' does not exist on type 'Element' elements[elements.length - 1].contentDocument.querySelector( target, ); } else { - // eslint-disable-next-line no-console - console.log(` Prior target is NOT iFrame`); element = document.querySelector(target); } - // eslint-disable-next-line no-console - console.log(` Element: `, element); if (element) { elements.push(element); } @@ -91,8 +83,6 @@ const mapResultsToIssues = ( results: axe.Result[], type: IssueType, ): Issue[] => { - console.log("Issues List: ", issuesList); - console.log("User Fixable Issues: ", issuesList["user-fixable"]); return results.map((result) => { const isUserFixable = type === "Alert" && @@ -125,7 +115,6 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { frameHasLoaded = frameDocument?.readyState === "complete"; } if (!frameHasLoaded) { - console.log("Frame has NOT loaded"); setTimeout(runAxeCore, 100, updateIssuesFn); return; } @@ -145,13 +134,13 @@ const runAxeCore = (updateIssuesFn: (issues: Issue[]) => void): void => { "Warning", ); const issues = violations.concat(incompletes); + // eslint-disable-next-line no-console console.log(` Issues: `, issues); if ( violations.length === 0 && incompletes.length === 0 && results.passes.length === 0 ) { - console.log("No valid results - Retrying..."); setTimeout(runAxeCore, 1500, updateIssuesFn); // No valid results indicates that content may not be fully loaded yet } else { updateIssuesFn(issues);