Skip to content

Assessment-блоки

Имена/команды: skin — wp2lms-turbo-blocks-mos (npm run build в каталоге пакета). Устаревшие build:all / wp2lms-turbo-pulse — см. обзор Turbo.

Как добавлять новые типы вопросов wp2turbo/* (например «Сортировка», «Сопоставление»), чтобы они:

  1. Работали standalone на странице (как сейчас choice / open).
  2. Встраивались в wp2turbo/question-bank (нумерация, «Далее», прогресс).
  3. Писали прогресс в ту же LMS-схему (questions / quiz), что Elementor / wp2lms-pro.

Эталонные реализации:

Тип Блок Alpine Twig Transformer
Выбор blocks/choice-question question-choice.js QuestionChoice.twig Wp2TurboChoiceQuestionTransformer
Открытый blocks/open-question question-open.js QuestionOpen.twig Wp2TurboOpenQuestionTransformer
Банк (оркестратор) blocks/question-bank + question-bank-questions question-bank.js QuestionBank.twig Wp2TurboQuestionBank*

Общий гайд по wp2turbo-блокам (tabs / icon-list): AI_TURBO_BLOCKS.md.
Контракт LMS store: LMS_QUIZ_STORAGE.md.

Что Путь
Манифест типов (allowlist + event prefix) blocks/_shared/question-types/registry.js
Sync factory (секции prompt/feedback/items) blocks/_shared/question-sync/create-question-sync.js
Банк слушает события из registry assets/src/frontend/question-bank.js
Status transformer (correct/incorrect/partial) Wp2TurboQuestionStatusTransformer (+ alias Wp2TurboOpenQuestionStatusTransformer)
Choice partial feedback blocks/choice-question-partial — только при selectionType=multiple + showFeedback; outcome partial (points=0)

Новый тип: сначала запись в registry.js, затем блок/Alpine/Twig; listeners банка и visibility подхватятся из манифеста.

Предварительные условия

Section titled “Предварительные условия”
  • Активны WP2LMS, WP2LMS Pro, Turbo, Blocks.
  • Сборка: npm run build:all (или build:skin после правок Alpine/блоков Blocks).
  • Не править wp-content/plugins/** вручную — только исходники + build.

Когда этого документа достаточно

Section titled “Когда этого документа достаточно”
Задача Достаточно?
Новый тип вопроса + совместимость с банком и LMS Да
Только UI/стили существующего choice/open Нет — править блок/CSS; банк трогать не нужно
Новый обычный wp2turbo-блок без проверки знаний Нет — AI_TURBO_BLOCKS.md
Менять контракт quiz / page.progress Нет — LMS_QUIZ_STORAGE.md + согласование с враппером

Архитектура: вопрос ↔ банк ↔ LMS

Section titled “Архитектура: вопрос ↔ банк ↔ LMS”
flowchart TB
subgraph editor [Gutenberg]
QB[question-bank-questions]
Q[wp2turbo/sort-question]
QB -->|InnerBlocks + factory| Q
end
subgraph front [Turbo front]
TwigQ[QuestionSort.twig]
AlpineQ[turboQuestionSort]
TwigB[QuestionBank.twig]
AlpineB[turboQuestionBank]
TwigQ --> AlpineQ
TwigB --> AlpineB
AlpineQ -->|"*:answered / *:next"| AlpineB
end
subgraph lms [wp2lms-pro]
QS["questions store"]
Quiz["quiz store"]
AlpineQ -->|writeQuestion| QS
AlpineB -->|addQuizScore / patchQuiz| Quiz
end
editor --> front

Правило баллов: один вопрос в банке = до 1 балла (points 0|1). Банк суммирует через addQuizScore({ scorePoints: 0|1 }).

Что Путь Назначение
Реестр типов blocks/_shared/question-types/registry.js blockName + eventPrefix (+ component); allowlist банка и Alpine-слушатели *:answered / *:next
Sync factory blocks/_shared/question-sync/create-question-sync.js createQuestionSync — секции prompt/body/feedback, child locks; используют sort/match/classify/choice *-sync.js
Prompt HTML (PHP) ResolvesQuestionPromptHtmlTrait Общий разбор HTML prompt в transformers
Status (PHP) Wp2TurboQuestionStatusTransformer correct/incorrect иконки; BC-alias: Wp2TurboOpenQuestionStatusTransformer

Новый тип: сначала запись в registry, затем sync через createQuestionSync (или тонкая обёртка), слушатели банка подтянутся из QUESTION_EVENT_PREFIXES.


Контракт событий (обязателен для банка)

Section titled “Контракт событий (обязателен для банка)”

Банк слушает на корне turboQuestionBank кастомные события (bubbling). Префиксы берутся из QUESTION_EVENT_PREFIXES — новый тип достаточно добавить в registry (плюс слать события с тем же eventPrefix).

Событие Когда detail (минимум)
{type}-question:answered После проверки ответа wp2Id, isCorrect (bool), hasFeedback (bool)
{type}-question:next Клик «Далее» в банке wp2Id, isCorrect

Эталон имён: choice-question:*, open-question:*. Для сортировки, например: sort-question:answered / sort-question:next.

Доп. поля в detail (по типу):

  • Choice: selected, selectedOptionWp2Ids
  • Open: answer, answerType
  • Sort (пример): order (массив id элементов) — банк их не требует, но полезны для отладки / resultPayload

Если hasFeedback === false, банк может сразу вызвать goNext() (как у choice без feedback).

Режим inQuestionBank После ответа
Standalone false Кнопка «Повторить» (reset())
В банке true Кнопка «Далее» (next() → событие *:next)

Прокидывать inQuestionBank в Alpine через Twig alpineConfig (как в QuestionChoice.twig / QuestionOpen.twig). В банке флаг выставляет PHP-transformer секции вопросов (inQuestionBank = true).


На submit() вопроса вызывать writeQuestion + buildQuestionState.

Минимальный shape (Elementor-совместимый):

{
isQuestionCompleted: true,
attemptsLeft: 0,
attemptStatus: 0,
nextButtonClicked: false,
answers: [], // choice: 1-based индексы; иначе []
points: 0 | 1,
showCorrectAnswersButton: false,
// расширения (не ломают Elementor readers):
// answer, answerType — open
// order: string[] — sort
// matchAnswers: { [promptId]: answerId } — match
// placement: { [itemId]: categoryId|null } — classify
}

После клика «Далее» в банке вызывать markQuestionNextClicked (nextButtonClicked: true). Если у вопроса showFeedback: false, банк сам вызывает markQuestionNextClicked перед auto-goNext. Правила:

  • Писать только при стабильном wp2Id (isStableLmsId в lms-quiz.js). Не использовать ephemeral q-{index} как ключ store.
  • На init() — readQuestion(wp2Id) и hydrate UI, если isQuestionCompleted.
  • Банк сам обновляет quiz через recordQuizAnswer по событию *:answered — вопрос не должен вызывать addQuizScore, если он внутри банка (иначе двойной счёт). Standalone вне банка пишет только questions.

Подробнее: LMS_QUIZ_STORAGE.md.


Чеклист: новый тип (пример «Сортировка»)

Section titled “Чеклист: новый тип (пример «Сортировка»)”

Имена ниже — шаблон. Замените sort / Sort / QuestionSort на свой slug.

A. Базовый wp2turbo-блок (как любой интерактив)

Section titled “A. Базовый wp2turbo-блок (как любой интерактив)”

См. AI_TURBO_BLOCKS.md. Кратко:

  1. blocks/sort-question/block.json + src/index.js (+ child-блоки при необходимости).
  2. Атрибут bankQuestionNumber (number, default 0) — для List View / заголовка в банке.
  3. Общий prompt: переиспользовать wp2turbo/question-prompt + getBankQuestionHeading при bankQuestionNumber > 0.
  4. components/QuestionSort/QuestionSort.twig + schema.json с "requiresAlpine": true (в turbo или Blocks override).
  5. assets/src/frontend/question-sort.js → Alpine.data('turboQuestionSort', …); регистрация в assets/src/entries/turbo.js.
  6. src/Mapping/Wp2TurboSortQuestionTransformer.php — props: wp2Id, inQuestionBank: false, labels, данные задания.
  7. src/Blocks/RegisterBlocks.php — BlockMapping + inserter allowlist (рядом с choice/open).

B. Совместимость с банком (обязательный слой)

Section titled “B. Совместимость с банком (обязательный слой)”

Без этих правок блок не появится в банке и не попадёт в прогресс теста.

# Куда Что сделать
1 blocks/_shared/question-types/registry.js Добавить { blockName, eventPrefix, component } — allowlist банка и Alpine listeners
2 add-questions-modal.js + index.js factories Опция в modal + QUESTION_FACTORIES → create…Block (allowlist уже из registry)
3 Тот же index.js → createQuestionBlocks Ветка factory → createSortQuestionBlock({ bankQuestionNumber })
4 Новый blocks/sort-question/src/sort-question-sync.js на createQuestionSync createSortQuestionBlock(attributes)
5 Wp2TurboQuestionBankQuestionsTransformer.php В QUESTION_BLOCKS: 'wp2turbo/sort-question' => 'QuestionSort'
6 — Слушатели в question-bank.js уже из registry (QUESTION_EVENT_PREFIXES)
7 question-bank/src/index.js Visibility / count уже через QUESTION_BLOCKS из registry
8 Twig QuestionSort inQuestionBank + кнопки Повторить / Далее; x-data с inQuestionBank
9 Alpine turboQuestionSort submit → writeQuestion + $dispatch('sort-question:answered', …); next → $dispatch('sort-question:next', …)

Нумерация «Вопрос №N» уже синхронится в question-bank-questions через bankQuestionNumber + managed heading prompt — factory должна передавать номер и использовать shared prompt.

Окно терминала
npm run build:skin # или build:all из корня
npm test --prefix wp2lms-turbo-pulse

Ручной чеклист:

  1. Inserter: блок виден вне банка.
  2. Банк → «Добавить вопрос» → тип «Сортировка» → несколько штук → заголовки №N, разные wp2Id.
  3. Пройти тест: ответ → «Далее» → следующий / results.
  4. DevTools: localStorage.questions / .quiz (вне курса) после ответа и finish.
  5. Reload: hydrate completed / статус банка.
  6. Retry: wipe + quiz:reset.

Минимальный скелет Alpine (шаблон)

Section titled “Минимальный скелет Alpine (шаблон)”
assets/src/frontend/question-sort.js
import { buildQuestionState, readQuestion, writeQuestion, isStableLmsId } from './lib/lms-quiz.js';
export function registerQuestionSort(Alpine) {
Alpine.data('turboQuestionSort', (initial = {}) => ({
answered: false,
isCorrect: false,
wp2Id: String(initial.wp2Id ?? '').trim(),
inQuestionBank: Boolean(initial.inQuestionBank),
showFeedback: initial.showFeedback !== false,
// … состояние задания …
init() {
this.hydrateFromLms();
},
hydrateFromLms() {
if (!isStableLmsId(this.wp2Id)) return;
const stored = readQuestion(this.wp2Id);
if (!stored?.isQuestionCompleted) return;
// восстановить UI из stored
this.isCorrect = Number(stored.points) > 0;
this.answered = true;
},
persistToLms() {
if (!isStableLmsId(this.wp2Id)) return;
writeQuestion(this.wp2Id, buildQuestionState({
isCorrect: this.isCorrect,
points: this.isCorrect ? 1 : 0,
selectedIndexes: [],
// order: [...],
}));
},
submit() {
if (this.answered) return;
this.isCorrect = /* проверка */;
this.answered = true;
this.persistToLms();
this.$dispatch('sort-question:answered', {
wp2Id: this.wp2Id,
isCorrect: this.isCorrect,
hasFeedback: this.showFeedback,
});
},
next() {
this.$dispatch('sort-question:next', {
wp2Id: this.wp2Id,
isCorrect: this.isCorrect,
});
},
reset() {
this.answered = false;
this.isCorrect = false;
},
}));
}

В question-bank.js слушатели уже из registry — после добавления eventPrefix отдельный addEventListener не нужен.


A11y (обязательный контракт для проверочных типов)

Section titled “A11y (обязательный контракт для проверочных типов)”

Общий хелпер: assets/src/frontend/lib/a11y.js.

Требование Как
Live-region На корне вопроса/банка: [data-a11y-live] role="status" aria-live="polite"
После submit announceAndFocusAfterAnswer → status [data-a11y-status] или Retry/Next [data-a11y-action]
После reset focusAfterReset → первый [data-a11y-control] / input / select / button
Банк next/start/results Фокус на [data-a11y-question-heading] / results title; progress aria-live
Sort Кнопки ↑/↓ + drag (Sortable). Объявление позиции в live-region
Classify <select> на карточке + drag. Drop zones с accessible name
Match Listbox: aria-expanded / aria-controls / role="option"; Esc + стрелки; restore focus
Имена Prompt → aria-labelledby (Choice fieldset, Open input). Не использовать placeholder как единственное имя
Focus visible :focus-visible на кастомных контролах в questions.css
Motion prefers-reduced-motion для анимации процента results

Новый тип обязан: live-region, status/action markers, keyboard-путь к полному ответу (не только pointer/drag), вызов announce/focus после submit/reset.


  • Не писать прогресс напрямую в localStorage / courseController — только через lms-quiz.js / pro globals.
  • Не вызывать addQuizScore из самого вопроса внутри банка.
  • Не полагаться на индекс в массиве как на id в LMS — только wp2Id.
  • Не добавлять тип только в modal без transformer QUESTION_BLOCKS — на фронте вопрос «пропадёт» из банка.
  • Не трогать Scene / page.progress из вопроса или банка.
  • Не выпускать Sort/Classify/Match без клавиатурной альтернативы drag/popup.

Сводка файлов для нового проверочного типа

Section titled “Сводка файлов для нового проверочного типа”
# Файл Standalone + Банк
0 blocks/_shared/question-types/registry.js — ✓
1 blocks/{name}/block.json + src/index.js ✓ ✓
2 blocks/{name}/src/{name}-sync.js (createQuestionSync) — ✓
3 src/Mapping/Wp2Turbo{Name}Transformer.php ✓ ✓ (inQuestionBank default false)
4 src/Blocks/RegisterBlocks.php ✓ ✓
5 components/Question{Name}/…twig ✓ ✓ (Далее / Повторить)
6 assets/src/frontend/question-{name}.js + turbo.js ✓ ✓ (события + LMS write + a11y)
7 question-bank-questions modal + factory — ✓
8 Wp2TurboQuestionBankQuestionsTransformer map — ✓
9 A11y: live-region, focus, keyboard path ✓ ✓
10 Unit/e2e при необходимости желательно желательно

После правок: npm run build:skin (блоки + Alpine) из wp2lms-turbo-blocks или build:all из корня.