generated from adobe/aem-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 192
[MWPW-171403][Preflight][Due diligence] Integrate full accessibility report in Preflight #3968
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
Merged
milo-pr-merge
merged 26 commits into
adobecom:stage
from
skumar09:accessibility-preflight
Jul 9, 2025
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
4502b2c
initial commit
1e35ee5
update review comments
cd65ade
update review comment-2
341927e
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
d6d616a
update review comments-3
2084082
add tags for color-contrast
65f32f8
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
23f8ada
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
1f9fa15
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
f7fc239
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
skumar09 edfb7a9
consider alt= as decorative images
skumar09 403619b
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
skumar09 6c83588
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
skumar09 e5b094b
merge existing img alt feature into new accessibility tab
skumar09 89bffe6
adust the preflight-full-width css
skumar09 4f46724
adust violation-column css bottom padding
skumar09 71f0530
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
skumar09 6ede750
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
skumar09 253e753
updated the tags
skumar09 551aedf
update the tags
skumar09 937ca08
rm color-contrast
skumar09 2f05883
remove console log
skumar09 5631172
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
skumar09 203e11f
include wcag22a and wcag22aa support
skumar09 d7ae5ab
Merge remote-tracking branch 'upstream/stage' into accessibility-pref…
skumar09 47ee0e0
comment update
skumar09 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
libs/blocks/preflight/accessibility/accessibility-config.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
export const AXE_CORE_CONFIG = { | ||
include: [['body']], | ||
exclude: [['.preflight'], ['aem-sidekick'], ['header'], ['.global-navigation'], ['footer'], ['.global-footer'], ['.mep-preview-overlay'], ['preflight-decoration']], | ||
runOnly: { | ||
type: 'tag', | ||
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'], | ||
}, | ||
}; | ||
|
||
export const CUSTOM_CHECKS_CONFIG = { | ||
checks: ['altText', 'color-contrast'], | ||
include: [['body']], | ||
exclude: [['.preflight'], ['aem-sidekick'], ['header'], ['.global-navigation'], ['footer'], ['.global-footer'], ['.mep-preview-overlay'], ['preflight-decoration']], | ||
}; |
27 changes: 27 additions & 0 deletions
27
libs/blocks/preflight/accessibility/accessibility-custom-checks.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { getFilteredElements } from './helper.js'; | ||
import checkImageAltText from './check-image-alt-text.js'; | ||
import checkKeyboardNavigation from './check-keyboard-navigation.js'; | ||
|
||
const checkFunctions = [ | ||
checkImageAltText, | ||
checkKeyboardNavigation, | ||
]; | ||
|
||
/** | ||
* Runs custom accessibility checks on filtered elements. | ||
* @param {Object} config - custom check config (checks, include, exclude). | ||
* @returns {Array} Violations from all custom checks. | ||
*/ | ||
async function customAccessibilityChecks(config = {}) { | ||
try { | ||
// Filter DOM elements based on include/exclude | ||
const elements = getFilteredElements(config.include, config.exclude); | ||
if (!elements.length) return []; | ||
return checkFunctions.flatMap((checkFn) => checkFn(elements, config)); | ||
} catch (error) { | ||
window.lana.log(`Error running custom accessibility checks: ${error.message}`); | ||
return []; | ||
} | ||
} | ||
|
||
export default customAccessibilityChecks; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
import { html, useState, useEffect } from '../../../deps/htm-preact.js'; | ||
import '../../../deps/axe.min.js'; | ||
import { AXE_CORE_CONFIG, CUSTOM_CHECKS_CONFIG } from './accessibility-config.js'; | ||
import customAccessibilityChecks from './accessibility-custom-checks.js'; | ||
import AuditImageAltText from './audit-image-alt-text.js'; | ||
|
||
/** | ||
* Runs the accessibility test using axe-core and custom checks. | ||
* @returns {Promise<Object>} Test results object (pass/fail + violations) or error. | ||
*/ | ||
async function runAccessibilityTest() { | ||
try { | ||
const results = await window.axe.run(AXE_CORE_CONFIG); | ||
const customViolations = await customAccessibilityChecks(CUSTOM_CHECKS_CONFIG); | ||
results.violations.push(...customViolations); | ||
return { | ||
pass: !results.violations.length, | ||
violations: results.violations, | ||
}; | ||
} catch (error) { | ||
window.lana.log(`Error running accessiblity test:, ${error}`); | ||
return { error: `Error running accessibility test: ${error.message}` }; | ||
} | ||
} | ||
|
||
/** | ||
* Preflight Accessibility Tab/Panel. | ||
*/ | ||
export default function Accessibility() { | ||
const [pageURL, setPageURL] = useState(window.location.href); | ||
const [testResults, setTestResults] = useState(null); | ||
const [loading, setLoading] = useState(false); | ||
const [expandedViolations, setExpandedViolations] = useState([]); | ||
|
||
useEffect(() => { | ||
const runTest = async () => { | ||
setLoading(true); | ||
setTestResults(null); | ||
setExpandedViolations([]); | ||
setPageURL(window.location.href); | ||
const results = await runAccessibilityTest(); | ||
setTestResults(results); | ||
setLoading(false); | ||
}; | ||
runTest(); | ||
}, []); | ||
const toggleViolation = (index) => { | ||
setExpandedViolations((prev) => { | ||
const isExpanded = prev.includes(index); | ||
return isExpanded ? prev.filter((i) => i !== index) : [...prev, index]; | ||
}); | ||
}; | ||
// Loading markup | ||
const loadingMarkup = () => html` | ||
<div class="preflight-columns"> | ||
<div class="preflight-column"> | ||
<p>Running Accessibility Test...</p> | ||
</div> | ||
</div> | ||
`; | ||
// Error markup | ||
const errorMarkup = (errorMsg) => html` | ||
<div class="preflight-columns"> | ||
<div class="preflight-column"> | ||
<div class="preflight-content-group"> | ||
<p class="preflight-item-title">Error</p> | ||
<p class="preflight-item-description">${errorMsg}</p> | ||
</div> | ||
</div> | ||
</div> | ||
`; | ||
// Results summary markup | ||
const resultsSummary = (results, url) => { | ||
if (!results) { | ||
return html` | ||
<div class="preflight-column"> | ||
<p>No accessibility results available.</p> | ||
</div> | ||
`; | ||
} | ||
return html` | ||
<div class="preflight-column"> | ||
<div class="preflight-content-group"> | ||
<div class="preflight-accessibility-item"> | ||
<div class="result-icon ${results.pass ? 'green' : 'red'}"></div> | ||
<div class="preflight-item-text"> | ||
<p class="preflight-item-title"> | ||
${results.pass | ||
? 'Accessibility Test Passed' | ||
: 'Accessibility Test Failed'} | ||
</p> | ||
<p class="preflight-item-description"> | ||
${results.pass | ||
? 'No accessibility issues found.' | ||
: `${results.violations.length} accessibility violations detected.`} | ||
</p> | ||
<ul class="summary-list"> | ||
<li><strong>Page:</strong> <a href="${url}" target="_blank">${url}</a></li> | ||
<li><strong>Test Scope:</strong> body</li> | ||
<li><strong>WCAG Tags:</strong> ${AXE_CORE_CONFIG.runOnly?.values?.join(', ') || 'NONE'}</li> | ||
</ul> | ||
<p class="preflight-accessibility-note"> | ||
<strong>Note:</strong> This test does not include screen reader behavior, focus order, or voice navigation checks. | ||
</p> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
`; | ||
}; | ||
// Violations markup | ||
const violationsList = (violations = []) => { | ||
if (!violations.length) { | ||
return html` | ||
<div class="preflight-column"> | ||
<p>No violations found.</p> | ||
</div> | ||
`; | ||
} | ||
return html` | ||
<div class="preflight-column"> | ||
<div class="preflight-content-group violations-section"> | ||
<div class="preflight-accessibility-item"> | ||
<div class="result-icon red"></div> | ||
<div class="preflight-item-text"> | ||
<p class="preflight-item-title">Accessibility Violations</p> | ||
<p class="preflight-item-description"> | ||
Click each violation to view details | ||
</p> | ||
</div> | ||
</div> | ||
|
||
<div class="violation-list"> | ||
${violations.map((violation, index) => { | ||
const isExpanded = expandedViolations.includes(index); | ||
return html` | ||
<div class="violation-item"> | ||
<div | ||
class="violation-summary ${isExpanded ? 'expanded' : ''}" | ||
=> toggleViolation(index)} | ||
> | ||
<div class="violation-expand"></div> | ||
<div class="preflight-content-heading"> | ||
<span class="violation-index">${index + 1}.</span> | ||
Violation [ | ||
<span class="severity ${violation.impact}">${violation.impact}</span> | ||
]: ${violation.description} | ||
</div> | ||
</div> | ||
|
||
${isExpanded && html` | ||
<div class="violation-details"> | ||
<ul class="violation-details-list"> | ||
<li><strong>Rule ID:</strong> ${violation.id}</li> | ||
<li><strong>Severity:</strong> <span class="severity ${violation.impact}">${violation.impact}</span></li> | ||
<li><strong>Fix: </strong> | ||
<a class="violation-link" href="${violation.helpUrl}" target="_blank"> | ||
More Info | ||
</a> | ||
</li> | ||
<li><strong>Affected Elements:</strong> | ||
<ul class="affected-elements"> | ||
${violation.nodes.map((node) => html`<li><code>${node.html}</code></li>`)} | ||
</ul> | ||
</li> | ||
</ul> | ||
</div> | ||
`} | ||
</div> | ||
`; | ||
})} | ||
</div> | ||
</div> | ||
</div> | ||
`; | ||
}; | ||
// Conditional rendering based on state | ||
if (loading) return loadingMarkup(); | ||
if (testResults?.error) return errorMarkup(testResults.error); | ||
if (!testResults) { | ||
return html` | ||
<div class="preflight-columns"> | ||
<div class="preflight-column"> | ||
<p>No accessibility test has been run yet.</p> | ||
</div> | ||
</div> | ||
`; | ||
} | ||
return html` | ||
<div class="preflight-columns accessibility-columns"> | ||
${resultsSummary(testResults, pageURL)} | ||
<div class="preflight-column violations-column"> | ||
${!testResults.pass && violationsList(testResults.violations)} | ||
</div> | ||
</div> | ||
<div class="preflight-full-width"> | ||
<${AuditImageAltText} /> | ||
</div> | ||
`; | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is is deliberate that you're not adding the keyboard checks as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vhargrave: Yes, it’s deliberate. Still exploring the use cases where axe-core doesn’t fully capture keyboard navigation issues when triggerred from browser/dom, will enable in subsequent pr's