- {showTimer && (
-
-
-
- {formatMmSs(showCountdown ? timeRemaining! : timeElapsed!)}
-
-
- )}
-
-
-
-
- {showLegend && (
- <>
-
setShowLegend(false)} />
-
-
{t(language, 'HELP_LEGEND_TITLE')}
-
- A
- {t(language, 'SECTION_CURRENT')}
-
-
- A
- {t(language, 'SECTION_DONE')}
-
-
- B
- {t(language, 'SECTION_UPCOMING')}
-
-
- >
- )}
-
-
-
- {questionNumber}/{totalQuestions}
-
-
-
-
-
- );
-}
diff --git a/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.module.scss b/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.module.scss
deleted file mode 100644
index d8714142..00000000
--- a/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.module.scss
+++ /dev/null
@@ -1,90 +0,0 @@
-@use '../../styles/variables' as v;
-
-.progressBar {
- display: flex;
- flex-direction: column;
- gap: v.$space-2;
- font-family: v.$ff-base;
-}
-
-.progressMeta {
- display: flex;
- justify-content: space-between;
- align-items: baseline;
-}
-
-.progressLabel {
- font-size: v.$text-xs;
- font-weight: v.$fw-semibold;
- color: v.$g600;
- text-transform: uppercase;
- letter-spacing: 0.04em;
-}
-
-.progressCount {
- font-size: v.$text-xs;
- font-weight: v.$fw-medium;
- color: v.$g500;
- font-variant-numeric: tabular-nums;
-}
-
-.track {
- width: 100%;
- height: 0.5rem;
- background: v.$gray-200;
- border-radius: v.$r-pill;
- overflow: hidden;
-}
-
-.fill {
- height: 100%;
- background: v.$brick;
- border-radius: v.$r-pill;
- transition: width v.$dur-base v.$ease-out;
-}
-
-.steps {
- display: flex;
- align-items: center;
- gap: v.$space-2;
- margin: 0;
- padding: 0;
- list-style: none;
-}
-
-.step {
- display: flex;
-}
-
-.dot {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 1.75rem;
- height: 1.75rem;
- border-radius: v.$r-pill;
- border: 1.5px solid v.$gray-300;
- background: v.$white;
- color: v.$g500;
- font-family: v.$ff-base;
- font-size: v.$text-xs;
- font-weight: v.$fw-semibold;
- font-variant-numeric: tabular-nums;
- transition: all v.$dur-fast v.$ease-out;
-}
-
-.active .dot {
- border-color: v.$brick;
- background: v.$brick;
- color: v.$white;
-}
-
-.completed .dot {
- border-color: v.$success;
- background: v.$success-bg;
- color: v.$success;
-}
-
-.upcoming .dot {
- opacity: 0.85;
-}
diff --git a/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.test.tsx b/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.test.tsx
deleted file mode 100644
index 8a3aa5da..00000000
--- a/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.test.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import { render, screen } from '@testing-library/react';
-import { ProgressBar, SectionSteps } from './ProgressIndicators';
-import type { Section } from '../../types';
-
-const mkSection = (id: string): Section => ({
- identifier: id,
- name: id,
- children: [],
- timeLimits: { max: 0, min: 0 },
- allowSkip: true,
- shuffle: false,
-});
-
-describe('ProgressBar', () => {
- it('exposes progressbar ARIA values', () => {
- render(
);
- const bar = screen.getByRole('progressbar');
- expect(bar).toHaveAttribute('aria-valuenow', '3');
- expect(bar).toHaveAttribute('aria-valuemax', '10');
- expect(bar).toHaveAttribute('aria-valuemin', '0');
- });
-});
-
-describe('SectionSteps', () => {
- it('marks the active section with aria-current', () => {
- const sections = [mkSection('a'), mkSection('b'), mkSection('c')];
- render(
-
,
- );
- const items = screen.getAllByRole('listitem');
- expect(items[1]).toHaveAttribute('aria-current', 'step');
- expect(items[0]).not.toHaveAttribute('aria-current');
- });
-});
diff --git a/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.tsx b/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.tsx
deleted file mode 100644
index 2a988db5..00000000
--- a/projects/sunbird-quml-player-react/src/components/ProgressIndicators/ProgressIndicators.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import { t, readI18n } from '../../i18n/translations';
-import type { Section } from '../../types';
-import styles from './ProgressIndicators.module.scss';
-
-/**
- * Progress indicators (spec §6.5) — pure, derived entirely from Context props.
- * Two exports:
- * - ProgressBar: answered/total bar (role="progressbar").
- * - SectionSteps: active/completed/upcoming section dots.
- */
-
-export interface ProgressBarProps {
- answered: number;
- total: number;
- language?: string;
-}
-
-export function ProgressBar({ answered, total, language = 'en' }: ProgressBarProps) {
- const pct = total > 0 ? Math.round((answered / total) * 100) : 0;
- return (
-
-
- {t(language, 'PROGRESS')}
-
- {answered} {t(language, 'OF')} {total}
-
-
-
-
- );
-}
-
-export interface SectionStepsProps {
- sections: Section[];
- currentSectionIndex: number;
- completed: boolean[];
- language?: string;
-}
-
-export function SectionSteps({
- sections,
- currentSectionIndex,
- completed,
- language = 'en',
-}: SectionStepsProps) {
- return (
-
- {sections.map((section, index) => {
- const state =
- index === currentSectionIndex
- ? 'active'
- : completed[index]
- ? 'completed'
- : 'upcoming';
- return (
- -
-
- {index + 1}
-
-
- );
- })}
-
- );
-}
diff --git a/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.module.scss b/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.module.scss
deleted file mode 100644
index ac82b310..00000000
--- a/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.module.scss
+++ /dev/null
@@ -1,67 +0,0 @@
-@use '../../styles/variables' as v;
-
-.body {
- font-family: v.$ff-question; // Noto Serif — editorial question stem (design)
- font-size: v.$text-base;
- line-height: v.$lh-normal;
- color: v.$g700;
- word-wrap: break-word;
- overflow-wrap: anywhere; // long URLs / unbroken strings never overflow
- max-width: 100%;
-
- // Images in body
- img {
- max-width: 100%;
- height: auto;
- margin: v.$space-3 0;
- border-radius: v.$r-sm;
- }
-
- // Tables — scroll horizontally inside their own box rather than the page.
- table {
- display: block;
- width: 100%;
- max-width: 100%;
- overflow-x: auto;
- -webkit-overflow-scrolling: touch;
- border-collapse: collapse;
- margin: v.$space-3 0;
-
- td,
- th {
- border: 1px solid v.$gray-200;
- padding: v.$space-2;
- text-align: left;
- }
- }
-
- // KaTeX — wide display math scrolls inside its own box (no page overflow).
- :global(.katex-display) {
- max-width: 100%;
- overflow-x: auto;
- overflow-y: hidden;
- padding-bottom: v.$space-1;
- }
-
- :global(.math-block) {
- max-width: 100%;
- overflow-x: auto;
- }
-
- // RTL support comes from dir="auto" on the container (direction resolved
- // from the content itself); `start` alignment follows whichever direction
- // the browser resolves, so tables align correctly in both scripts.
- table {
- td,
- th {
- text-align: start;
- }
- }
-
- // In an RTL UI (renderer wrapper sets dir="rtl") the body BLOCK aligns right
- // like the rest of the mirrored layout — while dir="auto" above still keeps
- // the CHARACTER order of English/math fallback content intact (1+1=?).
- [dir='rtl'] & {
- text-align: right;
- }
-}
diff --git a/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.test.tsx b/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.test.tsx
deleted file mode 100644
index c1a946a9..00000000
--- a/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.test.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { describe, it, expect } from 'vitest';
-import { render } from '@testing-library/react';
-import { QuestionBody } from './QuestionBody';
-
-describe('QuestionBody', () => {
- it('renders plain HTML body', () => {
- const { container } = render(
What is 2+2?' }} />);
- expect(container.textContent).toContain('What is 2+2?');
- });
-
- it('renders images from the body', () => {
- const { container } = render(' }} />);
- expect(container.querySelector('img')).toBeInTheDocument();
- });
-
- it('renders tables from the body', () => {
- const { container } = render(
- | cell |
' }} />,
- );
- expect(container.querySelector('table')).toBeInTheDocument();
- expect(container.textContent).toContain('cell');
- });
-
- it('uses dir="auto" so content direction follows the text (Arabic)', () => {
- const { container } = render(
- السلام عليكم' }} language="ar" />,
- );
- const root = container.firstElementChild;
- // dir="auto" (not a forced rtl): Arabic content resolves RTL, while
- // English/math fallback keeps its LTR character order (e.g. "1+1=?").
- expect(root).toHaveAttribute('dir', 'auto');
- expect(root).toHaveAttribute('lang', 'ar');
- });
-
- it('uses dir="auto" and the given lang for non-Arabic', () => {
- const { container } = render(Hello' }} />);
- const root = container.firstElementChild;
- expect(root).toHaveAttribute('dir', 'auto');
- expect(root).toHaveAttribute('lang', 'en');
- });
-
- it('renders KaTeX math from .math elements', () => {
- const { container } = render(
- x^2' }} />,
- );
- // KaTeX replaces the element content with .katex markup
- expect(container.querySelector('.katex')).toBeInTheDocument();
- });
-
- it('does not crash on empty body', () => {
- const { container } = render();
- expect(container.firstElementChild).toBeInTheDocument();
- });
-});
diff --git a/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.tsx b/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.tsx
deleted file mode 100644
index 5ddc4394..00000000
--- a/projects/sunbird-quml-player-react/src/components/QuestionBody/QuestionBody.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import { useEffect, useRef } from 'react';
-import katex from 'katex';
-import 'katex/dist/katex.min.css';
-import { resolveMediaHtml } from '../../utils/media';
-import type { MediaItem, MediaResolveContext } from '../../utils/media';
-import { readI18n } from '../../i18n/translations';
-import type { Question } from '../../types';
-import styles from './QuestionBody.module.scss';
-
-/**
- * QuestionBody — generic, shared renderer for question text.
- *
- * Responsibilities ONLY:
- * - Render raw HTML body (may contain images, tables, etc.)
- * - Render KaTeX math found in `.math` / `.math-block` elements
- * - RTL support (Arabic)
- *
- * It is question-type AGNOSTIC: no MCQ/SA/MTF/SEQ/REO/FTB logic, no answer
- * handling, no scoring/telemetry/navigation. Every Phase 4 question type reuses
- * this component to render its stem.
- */
-interface QuestionBodyProps {
- question: Pick;
- language?: string;
- /**
- * Media + offline resolution inputs. When supplied (e.g. by Hint for solution
- * content), its `media` takes precedence over `question.media`.
- */
- mediaCtx?: MediaResolveContext;
-}
-
-export function QuestionBody({ question, language = 'en', mediaCtx }: QuestionBodyProps) {
- const containerRef = useRef(null);
- // The HTML most recently written to the container. Parents rebuild the
- // `question`/`mediaCtx` props as fresh object literals on every render (and
- // the shell timer re-renders the tree every second), so the effect must NOT
- // rewrite innerHTML unless the RESOLVED content actually changed — rewriting
- // destroys and recreates embedded