From e7897de0a2c6053191ca4746660b9ccabbb80cc3 Mon Sep 17 00:00:00 2001 From: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:10:27 +0700 Subject: [PATCH] fix(a11y): fill in the blank challenges (#56775) Co-authored-by: Tom <20648924+moT01@users.noreply.github.com> Co-authored-by: Bruce Blaser --- client/i18n/locales/english/translations.json | 6 +- .../components/fill-in-the-blanks.tsx | 73 ++++++++++++------- .../Challenges/fill-in-the-blank/show.css | 9 ++- e2e/fill-in-the-blanks.spec.ts | 51 +++++++++++++ 4 files changed, 105 insertions(+), 34 deletions(-) create mode 100644 e2e/fill-in-the-blanks.spec.ts diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index 3ae980717fd..dfd72ee1447 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -527,8 +527,10 @@ "building-a-university": "We're Building a Free Computer Science University Degree Program 🎉", "if-help-university": "We've already made a ton of progress. Donate now to help our charity with the road ahead.", "preview-external-window": "Preview currently showing in external window.", - "fill-in-the-blank": "Fill in the blank", - "blank": "blank", + "fill-in-the-blank": { + "heading": "Fill in the blank", + "blank": "blank" + }, "quiz": { "correct-answer": "Correct!", "incorrect-answer": "Incorrect.", diff --git a/client/src/templates/Challenges/components/fill-in-the-blanks.tsx b/client/src/templates/Challenges/components/fill-in-the-blanks.tsx index e7b19a1c14c..549a0dead35 100644 --- a/client/src/templates/Challenges/components/fill-in-the-blanks.tsx +++ b/client/src/templates/Challenges/components/fill-in-the-blanks.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; - import { Spacer } from '@freecodecamp/ui'; + import { parseBlanks } from '../fill-in-the-blank/parse-blanks'; import PrismFormatted from '../components/prism-formatted'; import { FillInTheBlank } from '../../../redux/prop-types'; @@ -26,10 +26,14 @@ function FillInTheBlanks({ }: FillInTheBlankProps): JSX.Element { const { t } = useTranslation(); - const addInputClass = (index: number): string => { - if (answersCorrect[index] === true) return 'green-underline'; - if (answersCorrect[index] === false) return 'red-underline'; - return ''; + const getInputClass = (index: number): string => { + let cls = 'fill-in-the-blank-input'; + + if (answersCorrect[index] === false) { + cls += ' incorrect-blank-answer'; + } + + return cls; }; const paragraphs = parseBlanks(sentence); @@ -37,7 +41,7 @@ function FillInTheBlanks({ return ( <> - +
{paragraphs.map((p, i) => { @@ -47,36 +51,49 @@ function FillInTheBlanks({

{p.map((node, j) => { const { type, value } = node; - if (type === 'text') return value; - if (type === 'blank') + if (type === 'text') { + return value; + } + + // If a blank is answered correctly, render the answer as part of the sentence. + if (type === 'blank' && answersCorrect[value] === true) { return ( - + + {blankAnswers[value]} + ); + } + + return ( + + ); })}

); })}
- {showFeedback && feedback && ( - <> - - - - )} -
- {showWrong && {t('learn.wrong-answer')}} +
+ {showWrong && ( +
+ {t('learn.wrong-answer')} + +
+ )} + {showFeedback && feedback && }
); diff --git a/client/src/templates/Challenges/fill-in-the-blank/show.css b/client/src/templates/Challenges/fill-in-the-blank/show.css index 2a855dfad96..d6080bacdb7 100644 --- a/client/src/templates/Challenges/fill-in-the-blank/show.css +++ b/client/src/templates/Challenges/fill-in-the-blank/show.css @@ -36,10 +36,11 @@ z-index: 2; } -.green-underline { - border-bottom-color: var(--success-background) !important; +.correct-blank-answer { + color: var(--background-success) !important; + font-weight: bold; } -.red-underline { - border-bottom-color: var(--danger-background) !important; +.incorrect-blank-answer { + border-bottom-color: var(--background-danger) !important; } diff --git a/e2e/fill-in-the-blanks.spec.ts b/e2e/fill-in-the-blanks.spec.ts new file mode 100644 index 00000000000..8b04029faae --- /dev/null +++ b/e2e/fill-in-the-blanks.spec.ts @@ -0,0 +1,51 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Fill in the blanks challenge', () => { + test.beforeEach(async ({ page }) => { + await page.goto( + '/learn/a2-english-for-developers/learn-greetings-in-your-first-day-at-the-office/task-46' + ); + }); + + test('should display feedback if there is an incorrect answer', async ({ + page + }) => { + const blanks = page.getByRole('textbox', { name: 'blank' }); + + await blanks.first().fill('this'); // Answer the first blank correctly + await blanks.last().fill('bar'); // Answer the second blank incorrectly + await page.getByRole('button', { name: 'Check your answer' }).click(); + + await expect( + page.getByText("Sorry, that's not the right answer. Give it another try?") + ).toBeVisible(); + + // Once a blank is answered correctly, it is no longer rendered as an input field + await expect(blanks).toHaveCount(1); + + await expect(blanks.last()).toHaveAttribute('aria-invalid', 'true'); + }); + + test('should not display feedback if all blanks are answered correctly', async ({ + page + }) => { + const blanks = page.getByRole('textbox', { name: 'blank' }); + + await blanks.first().fill('this'); + await blanks.last().fill('those'); + await page.getByRole('button', { name: 'Check your answer' }).click(); + + // Close the completion modal + await page + .getByRole('dialog') + .getByRole('button', { name: 'Close' }) + .click(); + + await expect( + page.getByText("Sorry, that's not the right answer. Give it another try?") + ).toBeHidden(); + + // There aren't any blanks as all the inputs are rendered as text + await expect(blanks).toBeHidden(); + }); +});