feat(conflict): Add conflict resolution UI with viewport selection and route integration
[Description of changes]
What this MR adds
This adds a conflict review flow for Canvas Pages.
In simple terms: Canvas saves unpublished edits as auto-saves. A conflict happens when a user has unpublished Canvas changes, but the published Page is changed outside of that user’s Canvas session before they publish.
This MR shows those conflicts in Review changes, prevents conflicted items from being published directly, and lets the user open a side-by-side resolver. In the resolver, the user can choose either:
- Published version: discard the Canvas auto-save for that Page.
- New version: keep the Canvas auto-save and mark the conflict as resolved.
This first version is focused on canvas_page conflicts and is behind the canvas_dev_cd dev module.
Testing instructions
The main scenarios are covered by this Playwright E2E spec:
tests/src/Playwright/tests/isolatedPerTest/conflictResolution.spec.ts
Coverage map:
- Review panel shows conflict controls
Covers: one Page conflict, conflict banner, disabled checkbox, warning icon, row menu, and opening the resolver. - Side-by-side resolver handles multiple conflicts
Covers: two conflicts, Published/New comparison, review count, disabled Resolve button until selection, keeping the New version, discarding with the Published version, and the final resolved state. - Selected rows are unselected if they become conflicted
Covers: a normal selected pending change becoming conflicted, then being automatically unselected and blocked from publish. - Conflict UI stays hidden when the dev module is disabled
Covers: no conflict banner, no warning icon, and normal review-row behavior whencanvas_dev_cdis not enabled. - Legacy publish conflict errors still work
Covers: old publish-time 409 error behavior when the new conflict UI is disabled.
Manual smoke check, only if needed:
- Enable conflict detection:
ddev drush en canvas_dev_cd -y && ddev drush cr - Create one Page conflict and confirm Review changes can open the resolver.
- Cleanup:
ddev drush pm:uninstall canvas_dev_cd -y && ddev drush ev "\Drupal::keyValue('canvas.auto_save')->deleteAll();" && ddev drush cr
Closes #3591601