feat: #3593376 Iteration 2 - builder front-end theme CSS, AI component variants, and validation tests
Iteration 2 — builds on !1 (merged) (the module initialization + green QA/functional suite). Adds two fixes found through a deep Playwright‑MCP testing round on live DDEV sites for the UI Suite companion recipes (base / bootstrap / dsfr), more functional coverage, and an OOP‑hooks refactor.
What's in this iteration
1. Load the front‑end theme's CSS in the builder (fix)
The builder routes render in the admin theme (DisplayBuilderAdminThemeNegotiator) for consistent admin chrome + the navigation chatbot toggle. The side effect was that the default theme's global CSS — the stylesheet that styles the UI Suite components a user builds — was never attached, so cards/buttons rendered as unstyled markup in the canvas and Preview. hook_page_attachments() now re‑attaches the default theme's libraries on _display_builder_route routes (when default ≠ admin). Verified live: the AI‑built DSFR button renders as a blue‑France button (#000091, Marianne). Kernel‑tested.
2. Apply the SDC variant the AI chooses (fix)
ComponentSourceBuilder dropped the variant entirely — normalizeProps() correctly strips variant from props (UI Patterns stores it separately) but buildComponentSource() never wrote it back, so every AI‑placed component fell back to its default variant. On ui_suite_bootstrap the button's default variant adds no btn class at all, so AI‑built buttons rendered plain even when the agent asked for a primary one. It now writes a select‑sourced variant_id when the component declares that variant. Verified live: an AI‑built button now carries btn btn-primary. Kernel‑tested (valid / unknown / absent variant).
3. Comprehensive validation + user‑feedback scenarios (test)
New 07-…-validation-and-feedback.feature covering happy + sad paths explored live in the browser: valid settings save confirmation, the chat‑timeout range constraint, the required page‑layout label, the delete confirmation step, and the page‑layouts list actions. Assertions use language‑independent signals (constraints, the English entity label) so they pass on the French DSFR companion too.
4. Move the module hooks to an OOP Hook class (refactor)
display_builder_ai.module is removed; the three hooks (page_attachments, page_bottom, deepchat_settings) and their helpers now live in src/Hook/DisplayBuilderAiHooks.php using the Drupal 11 #[Hook] attribute, with every collaborator supplied by constructor dependency injection instead of \Drupal:: static calls (registered explicitly in …services.yml). deepchat_settings still fires because ai_chatbot invokes it via ModuleHandler::invokeAll(). The module now has no procedural code, and the attach is scoped to builder routes with a theme cache context. Verified live (all three hooks report implemented; the builder still loads the theme CSS, renders the chat block and attaches its settings); the kernel test invokes the hook through the service.
Notes
- The UI Suite framework assets (
/libraries/dsfr,/libraries/bootstrap) are not auto‑installed bycomposer requireof the companion recipes; a site needs them for styled rendering (the themes declare them as root‑composer packages). - Known cosmetic gap: in the builder preview on Bootstrap, Claro's admin button CSS still out‑specifies
.btn-primary(it computes grey there); the markup is correct and the real front‑end renders blue. A CSS load‑order/scoping pass could close this — left out of this iteration.
Checkpoints
- Addition/Change/Update/Fix
- Testing to ensure no regression
- Automated unit testing coverage
- Automated functional testing coverage
- UX/UI designer responsibilities
- Readability
- Accessibility
- Performance
- Security
- Documentation
- Code review by maintainers
- Full testing and approval
- Credit contributors
- Review with the product owner
- Release notes snippet
- Release
Related to #3593376