Skip to content
This repository was archived by the owner on Jul 16, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion projects/sunbird-quml-player-react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@project-sunbird/sunbird-quml-player-web-component-react",
"private": true,
"version": "0.1.9",
"version": "0.1.10",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@
height: 100%;
width: 100%;
max-width: 100%;
// Query container for the player's responsive rules (m.mobile / m.tablet).
// `inline-size` contains ONLY the inline (width) axis, so the height:100%
// chain — and the :host height:100% freeze fix — is untouched. Every
// responsive component is a descendant of this element, so they respond to
// the player's own available width instead of the browser viewport (needed
// for the editor's narrow mobile preview).
container-type: inline-size;
container-name: quml;
// Positioning context for the player's overlays. The overlays (submit modal,
// mobile sections drawer, feedback toast) are `position: absolute` and anchor
// to THIS box, so they stay within the player instead of covering the whole
// browser window when the player is embedded in a small frame (editor mobile
// preview). When the player fills the viewport (portal, real mobile,
// standalone) this box == the viewport, so it is visually identical to the
// previous `position: fixed` behavior. (container-type does not reliably
// establish a fixed-positioning containing block across browsers, so we rely
// on absolute positioning + this explicit relative anchor instead.)
position: relative;
overflow-x: hidden; // hard guard: nothing scrolls the shell sideways
// Overview / results / review render their screen directly in the shell (the
// assessment stage scrolls via its inner .main). When the host constrains the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@
@use '../../styles/mixins' as m;

// Mobile sections navigator — bottom sheet (matches the design's `sheetUp`).
// `absolute` (anchored to MainPlayer's .appShell), not `fixed`, so the sheet
// stays within the player box when embedded in a small frame (editor preview).
// When the player fills the viewport (.appShell == viewport) it is identical to
// fixed.
.overlay {
position: fixed;
position: absolute;
inset: 0;
z-index: 1000;
display: flex;
Expand All @@ -15,7 +19,11 @@
display: flex;
flex-direction: column;
width: 100%;
max-height: 80vh;
// `%` (not `vh`) so the sheet height tracks the player box, not the browser
// viewport: in a short embedded frame it can't grow taller than .appShell.
// The overlay is absolute inset:0 (== .appShell height), so 80% == 80% of the
// player; identical to `80vh` when the player fills the viewport.
max-height: 80%;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
background: v.$ivory;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
@use '../../styles/variables' as v;
@use '../../styles/mixins' as m;

// `absolute` (anchored to MainPlayer's .appShell), not `fixed`, so the modal
// stays within the player box when embedded in a small frame (editor preview).
// When the player fills the viewport (.appShell == viewport) it is identical to
// fixed.
.overlay {
position: fixed;
position: absolute;
inset: 0;
z-index: 1200;
display: flex;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
@use '../../styles/variables' as v;
@use '../../styles/mixins' as m;

// `absolute` (anchored to MainPlayer's .appShell), not `fixed`, so the toast
// stays within the player box when embedded in a small frame (editor preview).
// When the player fills the viewport (.appShell == viewport) it is identical to
// fixed.
.viewport {
position: fixed;
position: absolute;
bottom: v.$space-8;
left: 50%;
transform: translateX(-50%);
z-index: 1100;
display: flex;
justify-content: center;
width: max-content;
max-width: min(92vw, 36rem);
// `%` (not `vw`) so the cap tracks the player box, not the browser viewport:
// in a narrow embedded frame the toast stays within .appShell. Identical to
// `92vw` when the player fills the viewport (.appShell == viewport).
max-width: min(92%, 36rem);
pointer-events: none;
}

Expand Down
14 changes: 12 additions & 2 deletions projects/sunbird-quml-player-react/src/styles/mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,26 @@
@use 'variables' as v;

// Respond below the mobile breakpoint (≤768px).
//
// Uses a CONTAINER query (against the `quml` container declared on MainPlayer's
// `.appShell`), NOT a viewport `@media`. The player is an embeddable web
// component: hosts (e.g. the Sunbird editor's mobile preview) render it in a
// narrow box while the browser viewport stays desktop-width, so viewport media
// queries never fired and the layout overflowed. Querying the player's own
// width makes it responsive to the space it's actually given.
// When the player fills the viewport (portal, real mobile, standalone) the
// container width == viewport width, so the breakpoints trigger identically to
// before — no behavior change in those environments.
@mixin mobile {
@media (max-width: v.$bp-mobile) {
@container quml (max-width: #{v.$bp-mobile}) {
@content;
}
}

// Respond at tablet and below (≤1023px). Combine with `mobile` (which is more
// specific / placed after) for progressive collapse: tablet 2-up → mobile 1-up.
@mixin tablet {
@media (max-width: v.$bp-tablet - 1px) {
@container quml (max-width: #{v.$bp-tablet - 1px}) {
@content;
}
}
Expand Down
16 changes: 10 additions & 6 deletions projects/sunbird-quml-player-react/src/utils/score.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ describe('score utils (Angular evaluateAutoScored parity, normalized to 0..1)',
expect(calculateMTFScore(q, { matches: { a: '1', b: '2' } })).toBe(1); // min(6,4)/4
});

it('MTF legacy: proportional round(maxScore × hits/total)', () => {
it('MTF partial disabled (not MAP_RESPONSE): all-or-nothing', () => {
const q = mk(
{
response1: {
Expand All @@ -100,8 +100,10 @@ describe('score utils (Angular evaluateAutoScored parity, normalized to 0..1)',
},
{ maxScore: 10 },
);
// 2 of 4 → round(10 × 0.5) = 5 → 0.5
expect(calculateMTFScore(q, { matches: { a: '1', b: '2', c: 'x', d: 'y' } })).toBe(0.5);
// 2 of 4 correct → no partial credit → 0.
expect(calculateMTFScore(q, { matches: { a: '1', b: '2', c: 'x', d: 'y' } })).toBe(0);
// All 4 correct → full marks → 1.
expect(calculateMTFScore(q, { matches: { a: '1', b: '2', c: '3', d: '4' } })).toBe(1);
});

// ── FTB (ftb) ────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -150,16 +152,18 @@ describe('score utils (Angular evaluateAutoScored parity, normalized to 0..1)',
expect(calculateFTBScore(q, { responses: { response1: 'red', response2: 'red' } })).toBe(0.5);
});

it('FTB legacy: proportional round(maxScore × hits/total)', () => {
it('FTB partial disabled (not MAP_RESPONSE): all-or-nothing', () => {
const q = mk(
{
response1: { cardinality: 'single', type: 'string', correctResponse: { value: 'cat' } },
response2: { cardinality: 'single', type: 'string', correctResponse: { value: 'dog' } },
},
{ maxScore: 4 },
);
// 1 of 2 → round(4 × 0.5) = 2 → 0.5
expect(calculateFTBScore(q, { responses: { response1: 'cat', response2: 'fish' } })).toBe(0.5);
// 1 of 2 correct → no partial credit → 0.
expect(calculateFTBScore(q, { responses: { response1: 'cat', response2: 'fish' } })).toBe(0);
// Both correct → full marks → 1.
expect(calculateFTBScore(q, { responses: { response1: 'cat', response2: 'dog' } })).toBe(1);
expect(calculateFTBScore(q, null)).toBe(0);
});

Expand Down
Binary file modified projects/sunbird-quml-player-react/src/utils/score.ts
Binary file not shown.
Loading