fix(a11y): improve challenge test suite results accessibility (#45802)

Co-authored-by: moT01 <20648924+moT01@users.noreply.github.com>
pull/46483/head
Bruce Blaser 2022-06-13 23:39:25 -07:00 committed by GitHub
parent 0e6e103d32
commit 46450b802c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 41 deletions

View File

@ -457,7 +457,9 @@
"donate": "Donate with PayPal",
"fail": "Test Failed",
"not-passed": "Not Passed",
"waiting": "Waiting",
"passed": "Passed",
"failed": "Failed",
"hint": "Hint",
"heart": "Heart",
"initial": "Initial",

View File

@ -3,6 +3,12 @@
justify-content: flex-start;
flex-direction: column;
margin: 15px 0;
padding-left: 0;
}
.challenge-test-suite-heading {
font-size: 0.9em;
text-align: center;
}
.challenge-test-suite code {

View File

@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import Fail from '../../../assets/icons/fail';
import GreenPass from '../../../assets/icons/green-pass';
@ -6,7 +7,6 @@ import Initial from '../../../assets/icons/initial';
import './test-suite.css';
import { ChallengeTest, Test } from '../../../redux/prop-types';
type TestSuiteTest = {
err?: string;
pass?: boolean;
@ -20,47 +20,39 @@ interface TestSuiteProps {
tests: Test[];
}
function getAccessibleText(text: string, err?: string, pass?: boolean) {
let accessibleText = 'Waiting';
const cleanText = text.replace(/<\/?code>/g, '');
// Determine test status (i.e. icon)
if (err) {
accessibleText = 'Error';
} else if (pass) {
accessibleText = 'Pass';
}
// Append the text itself
return accessibleText + ' - ' + cleanText;
}
function TestSuite({ tests }: TestSuiteProps): JSX.Element {
const { t } = useTranslation();
const testSuiteTests = tests.filter(isTestSuiteTest);
return (
<div className='challenge-test-suite'>
{testSuiteTests.map(({ err, pass = false, text = '' }, index) => {
const isInitial = !pass && !err;
const statusIcon = pass && !err ? <GreenPass /> : <Fail />;
return (
<div
aria-label={getAccessibleText(text, err, pass)}
className='test-result'
key={text.slice(-6) + String(index)}
>
<div className='test-status-icon'>
{isInitial ? <Initial /> : statusIcon}
</div>
<div
aria-hidden='true'
className='test-output'
dangerouslySetInnerHTML={{ __html: text }}
/>
</div>
);
})}
</div>
<>
<h2 className='challenge-test-suite-heading'>Tests</h2>
<ul className='challenge-test-suite'>
{testSuiteTests.map(({ err, pass = false, text = '' }, index) => {
const isInitial = !pass && !err;
const statusIcon = pass && !err ? <GreenPass /> : <Fail />;
const initialText = t('icons.waiting');
const statusText =
pass && !err ? t('icons.passed') : t('icons.failed');
// Remove opening/closing <p> so screen reader will read both
// status message and test text as one block.
text = text.replace(/^<p>|<\/p>$/g, '');
return (
<li className='test-result' key={text.slice(-6) + String(index)}>
<div className='test-status-icon' aria-hidden='true'>
{isInitial ? <Initial /> : statusIcon}
</div>
<div className='test-output'>
<span className='sr-only'>
{isInitial ? initialText : statusText}:{' '}
</span>
<span dangerouslySetInnerHTML={{ __html: text }} />
</div>
</li>
);
})}
</ul>
</>
);
}

View File

@ -8,11 +8,13 @@ describe('Help Button', () => {
it('should toggle the dropdown menu', () => {
cy.get('#get-help-dropdown').scrollIntoView().click();
cy.get('ul[role="menu"]').should('be.visible');
cy.get('.tool-panel-group ul[role="menu"]')
.scrollIntoView()
.should('be.visible');
});
it('should render three links when video is available', () => {
cy.get('ul[role="menu"]').within(() => {
cy.get('.tool-panel-group ul[role="menu"]').within(() => {
cy.get('a').should('have.length', 3);
cy.get('a').eq(0).contains('Get a Hint');
cy.get('a').eq(1).contains('Watch a Video');
@ -25,7 +27,7 @@ describe('Help Button', () => {
'/learn/front-end-development-libraries/bootstrap/apply-the-default-bootstrap-button-style'
);
cy.get('#get-help-dropdown').scrollIntoView().click();
cy.get('ul[role="menu"]').within(() => {
cy.get('.tool-panel-group ul[role="menu"]').within(() => {
cy.get('a').should('have.length', 2);
cy.get('a').eq(0).contains('Get a Hint');
cy.get('a').eq(1).contains('Ask for Help');