From 1c96b33328e0e100567d50b8c4d6dc8faedb0d60 Mon Sep 17 00:00:00 2001 From: Abhishek-Punhani Date: Mon, 17 Aug 2026 22:35:24 +0530 Subject: [PATCH] feat: introduce ClickableRegion component to improve keyboard accessibility for interaction editors Signed-off-by: Abhishek-Punhani --- .../__tests__/ClickableRegion.spec.js | 69 ++++++++++++++ .../components/ClickableRegion/index.vue | 93 +++++++++++++++++++ .../choice/ChoiceInteractionEditor.vue | 71 +++++++------- .../__tests__/ChoiceInteractionEditor.spec.js | 26 ++++++ .../ordering/OrderingInteractionEditor.vue | 40 +++++--- .../textEntry/TextEntryEditor.vue | 34 ++++--- .../views/QTIEditor/qtiEditorStrings.js | 8 ++ 7 files changed, 281 insertions(+), 60 deletions(-) create mode 100644 contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ClickableRegion/__tests__/ClickableRegion.spec.js create mode 100644 contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ClickableRegion/index.vue diff --git a/contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ClickableRegion/__tests__/ClickableRegion.spec.js b/contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ClickableRegion/__tests__/ClickableRegion.spec.js new file mode 100644 index 0000000000..e8e5589e38 --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ClickableRegion/__tests__/ClickableRegion.spec.js @@ -0,0 +1,69 @@ +import { render, screen, fireEvent } from '@testing-library/vue'; +import VueRouter from 'vue-router'; +import ClickableRegion from '../index.vue'; + +describe('ClickableRegion', () => { + it('renders a button with the given aria-label', () => { + render(ClickableRegion, { + props: { + ariaLabel: 'Test label', + }, + routes: new VueRouter(), + }); + + expect(screen.getByRole('button', { name: 'Test label' })).toBeInTheDocument(); + }); + + it('does not render the button when suppressed is true', () => { + render(ClickableRegion, { + props: { + ariaLabel: 'Test label', + suppressed: true, + }, + routes: new VueRouter(), + }); + + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('emits a single click event on mouse click', async () => { + const { emitted } = render(ClickableRegion, { + props: { + ariaLabel: 'Test label', + }, + routes: new VueRouter(), + }); + + await fireEvent.click(screen.getByRole('button')); + + expect(emitted().click).toHaveLength(1); + }); + + it('emits a single click event on Enter key', async () => { + const { emitted } = render(ClickableRegion, { + props: { + ariaLabel: 'Test label', + }, + routes: new VueRouter(), + }); + + const button = screen.getByRole('button'); + await fireEvent.click(button); + + expect(emitted().click).toHaveLength(1); + }); + + it('emits a single click event on Space key', async () => { + const { emitted } = render(ClickableRegion, { + props: { + ariaLabel: 'Test label', + }, + routes: new VueRouter(), + }); + + const button = screen.getByRole('button'); + await fireEvent.click(button); + + expect(emitted().click).toHaveLength(1); + }); +}); diff --git a/contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ClickableRegion/index.vue b/contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ClickableRegion/index.vue new file mode 100644 index 0000000000..7a0616624b --- /dev/null +++ b/contentcuration/contentcuration/frontend/shared/views/QTIEditor/components/ClickableRegion/index.vue @@ -0,0 +1,93 @@ + + + + + + + diff --git a/contentcuration/contentcuration/frontend/shared/views/QTIEditor/interactions/choice/ChoiceInteractionEditor.vue b/contentcuration/contentcuration/frontend/shared/views/QTIEditor/interactions/choice/ChoiceInteractionEditor.vue index bc1edad73d..ff72914d66 100644 --- a/contentcuration/contentcuration/frontend/shared/views/QTIEditor/interactions/choice/ChoiceInteractionEditor.vue +++ b/contentcuration/contentcuration/frontend/shared/views/QTIEditor/interactions/choice/ChoiceInteractionEditor.vue @@ -27,9 +27,11 @@ -
@@ -42,6 +44,7 @@ :minHeight="'80px'" :autofocus="mode === 'edit' && isQuestionOpen" :imageProcessor="EditorImageProcessor" + :tabindex="-1" class="editor" @update="setPrompt" @minimize="closeQuestion" @@ -49,7 +52,7 @@
- + @@ -90,11 +93,13 @@ class="choice-group" > -
{{ errorDuplicateChoiceContent$() }} -
+
@@ -200,7 +206,7 @@