-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Die Logik des Attempt-Iframe lebt aktuell in einem jinja2-Template, was historisch gewachsen, aber eigentlich suboptimal ist.
Diese Nachteile sehe ich im aktuellen Ansatz:
- "Template-Abuse": Jinja wird verwendet, um JS-Code zu generieren.
- Type safety: Inline JS im Template ist nicht statisch analysierbar.
- Testability: Nur schwer oder gar nicht zu testen.
- Global Pollution:
attemptwird als globale Variable gesetzt. - Framework boundary: Der Inhalt des iframe's sollte als separate (Mini-)Applikation angesehen werden.
- Unnötige "Cross-project" Code-Duplizierung zwischen SDK und Moodle Plugin.
Die Idee wäre, eine "Mini-Lib", geschrieben in TypeScript zu erstellen, die vom SDK (und Moodle-Plugin?) verwendet wird.
- Verantwortlichkeiten klar trennen:
- Host application (Vue SPA, Moodle, ...)
- spricht via
postMessagemit dem iframe - greift niemals direkt in den iframe DOM.
- spricht via
- Question iframe
- unabhängige "Mini-Applikation"
- exposed nötige JS-API (Attempt-Objekt, Formdaten, resize, theme switch, etc.)
- Führt zusätzlichen JS-Code vom QPy-Package im iframe aus.
- Host application (Vue SPA, Moodle, ...)
Alles was Logik/Verhalten ist, wandert außerhalb des iframe in eine "Mini-Lib". Die wird einmal gebundled und als static asset serviert.
Das Jinja2-Template wird stark vereinfacht und enthält ausschließlich Daten, etwa
{% extends "page.html.jinja2" %}
{% block head %}
{{ super() }}
<base target="_blank">
<link rel="stylesheet" href="{{ static_url('question-iframe.css') }}">
{% for url in stylesheet_urls %}<link rel="stylesheet" href="{{ url|safe }}">{% endfor %}
{% endblock head %}
{% block content %}
<div id="qpy-root">
{# Question formulation, feedback, etc. (wie bisher) #}
</div>
{# Data-only #}
<script type="application/json" id="qpy-runtime-data">
{
"displayOptions": {{ display_options|tojson }},
"javascriptCalls": {{ javascript_calls|tojson }},
}
</script>
<script type="importmap">
{ "imports": {{ import_map|tojson }} }
</script>
{# Runtime bundle #}
<script type="module" src="/static/iframe-runtime.js"></script>
{% endblock content %}@questionpy/iframe-runtime Package
Skizze:
# Python SDK / Moodle plugin
npm install @questionpy/iframe-runtime/questionpy-iframe-runtime
├─ src
│ ├─ index.ts # Public API (bootstraps the iframe)
│ ├─ Attempt.ts # Attempt class
│ ├─ events.ts # postMessage protocol
│ ├─ resize.ts # ResizeObserver logic
│ ├─ theme.ts # Color mode/custom theme logic
│ ├─ ...
│ └─ types.d.ts # Shared type definitions
├─ package.json
├─ tsconfig.json
└─ rollup.config.ts
@questionpy/iframe-runtime lebt entweder innerhalb des SDKs, oder wenn das Moodle-Plugin auch diese Lib verwenden soll (m.M.n. die präferierte Variante), dann könnte @questionpy/iframe-runtime evtl. auch in ein eigenes Repository wandern.
Integration mit Host-Apps
- Host-Apps können mit Hilfe des Templates/Render-Logik Einfluss auf die DOM-Struktur des iframe-Inhalts nehmen und bei Bedarf auch custom CSS injizieren.
- Je nach Host-App beinhaltet das Plugin (oder SDK in diesem Fall) JavaScript-Adapter-Code, der mit dem iframe spricht.
- Rollup baut verschiedene (ESM-, AMD-, iife-Module, ...), so dass die LMS und ihre Modulsystem-Constraints abgedeckt werden.
Integration mit Question-Package-Code
Die Lib bietet eine einheitliche API, um Paketen ein Interface zum Attempt-Objekt (und zukünftig evtl. noch anderen Dingen?) zu bieten. Sollte einen Promise zurückliefern, so dass sichergestellt ist, dass der Code komplett initialisiert und bereit ist, etwa
import { getAttempt } from '@questionpy/iframe-runtime'
const attempt = await getAttempt()Vorteile
- Type Safety: Volle Coverage für die iframe-Runtime-Logik
- Entkoppelte Architektur: Klare Trennung zwischen
- Host-App (SDK bzw. LMS)
- Iframe-Runtime (generisches TypeScript)
- Question Package (HTML/JS/CSS)
- Code wiederverwendbar zwischen SDK und LMS-Plugins
- Testability: Runtime-Logik kann komplett "ge-unit-tested" werden
- Klar definierte API zwischen
- Host und iframe-Runtime (Message passing)
- Paket- und iframe-Runtime-Code
- (beide APIs könnten bei Bedarf versioniert werden)