great, that works well!
The preview is empty for me when using nuxt-previewer, so there is some issue with that. But that should a separate issue. Done then!
Merged!
Works great now! Canvas brings all the API we need, we just need to use it correctly.
Except that I think you are looking to have those JS components live in code (and git), and not in config entities. Is that right?
Yes, the idea is that you can use whatever external tooling, IDE, frameworks etc as you like, as long as it produces a valid JavaScript file/bundle. I defined a loose contract so all that is required is defining a render() function. That way, it should be possible to wrap any kind of framework easily.
So, as you point out, code components are/will be flexible, generic JS as well, code-wise, but as I understand it's one fixed bundling process / tooling that is used.
Running it some issue when testing this on an article content type with a tags field.
Manage custom element dies, with exception:
The website encountered an unexpected error. Try again later.
Drupal\Core\Entity\EntityMalformedException: The "taxonomy_term" entity cannot have a URI as it does not have an ID in Drupal\Core\Entity\EntityBase->toUrl() (line 177 of core/lib/Drupal/Core/Entity/EntityBase.php).
Drupal\custom_elements\Processor\DefaultFieldItemProcessor->addtoElement() (Line: 327)
Drupal\custom_elements\CustomElementGenerator->process() (Line: 71)
Drupal\custom_elements\Processor\DefaultFieldItemListProcessor->addtoElement() (Line: 327)
Drupal\custom_elements\CustomElementGenerator->process() (Line: 59)
Drupal\custom_elements\Plugin\CustomElementsFieldFormatter\AutoCeFieldFormatter->build() (Line: 302)
Drupal\custom_elements\CustomElementGenerator->buildEntityComponentFields() (Line: 262)
Drupal\custom_elements\CustomElementGenerator->buildEntityContent() (Line: 205)
Drupal\custom_elements\CustomElementGenerator->generateMultiple() (Line: 178)
Drupal\custom_elements\CustomElementGenerator->generate() (Line: 1232)
Drupal\custom_elements_ui\Form\EntityCustomElementsDisplayEditForm->buildPreview() (Line: 443)
Drupal\custom_elements_ui\Form\EntityCustomElementsDisplayEditForm->form() (Line: 107)
Drupal\Core\Entity\EntityForm->buildForm()
The exception is NOT catched, so it fatals out.
Seems like just cathing RunTimeException wasn't a good idea, sry for that. Should explicitely catch EntityMalFormedExceptions or there others that we need to catch also?
Testing it on a simple page content type, I don't get it to work. The Preview field-set is there, but it's empty always?
MR looks all good now, thanks! Taking a test drive now..
So this got moved to
https://www.drupal.org/project/canvas_extjs →
for now.
Not yet tested with beta2, but works fine with beta1. It integrates nicely with Lupus Decoupled, but needs dev-releases for now. See
here
📌
First-class experience builder support
Active
for setup steps if you want to give it a try!
Got this complete and working. Also improved docs. Ready for final tweaks!
Thank you, much better! I did another review and left a couple of remarks at the PR.
The patch has been mostly written by arthur_lorenz, and be reviewed and slightly improved by me. I added credits for arthur.
Updating setup instructions for testing. Assuming 📌 Allow selection of suiting preview providers Active is merged.
Test setup
- Install custom_elements module with latest 3.x dev version + lupus_decoupled with latest 1.x-dev
- Install drupal canvas + https://www.drupal.org/project/issues/canvas_extjs →
- Install drupal and enable both modules
- Launch a test-frontend with https://github.com/drunomics/nuxt-component-preview configured. Use alpha9 or later.
The easiest way to do that is to clone https://github.com/drunomics/nuxt-component-preview and to use its dev-playground via `npm run dev`. However, you need to make sure CORS is configured correctly. For that, copy the example CORS config from the README into nuxt.config.ts - with your drupal base URL - Go to Lupus Decoupled settings screen, configure your base-url (e.g. https://localhost:3000) and select Nuxt as preview provider.
- Register components in Drupal canvas. For now this only works via drush:
ddev drush canvas:extjs-register https://yourfrontend.com/component-index.json
Component registration is done and works nicely now also - see 📌 Ease registration via component index Active .
got it implemented, it's rather straight-forward - some care must be taken for installations with custom_elements module versions <3.2 which do not have the new API. I tested the code with both versions, works fine.
For gitlab-ci runs, I had a first green run without current stable custom_elements version, besides some phpstan complaining about using unexiting APIs... I'm making it work with the version now so it can run against that.
yes, that's definitely possible. However, atm it's nothing that is supported out of the box, so you need a custom module to make it work.
We have been doing this with lupus-decoupled a lot in the past.
Extension points:
- https://git.drupalcode.org/project/lupus_decoupled/-/blob/1.x/modules/lu... - decorate the service to customize it
- https://git.drupalcode.org/project/lupus_decoupled/-/blob/1.x/modules/lu... and/or add URLs here
Then, you need to handle which content goes onto which domain. One simple, but not perfect approach is to add a common prefix in URL-aliases of your content, e.g. prefix /site1, then use api-path prefix, e.g. /ce-api/site1 in your nuxt site. Not perfect, since it obviously would only work for the aliased urls, not others.
Integration with domain module would probably make more sense and should be rather straight-forward, possible even out-of-the-box, there is an issue 📌 Support domain module Active . Happy to help getting this work if you would like to go for that.
Please re-open if you have more questions and feel free to reach out at slack!
I think this can be much simplified by being based solely on the preview-API which already landed in 3.x + existing render logic + existing sample-values of core. It seems like all we should have to add is some glue-code and UI on top?
I got i working quite nicely! :-)
Based upon the vue-team supported vue-component-meta package we are able to read most metadata. Generally, it buidls upon the typescript definitons and also supports enums that way. @example and @enum-labels are supported via js-tags. Given that, the generated file is served from nuxt and can be used to easily register files via drush!
https://github.com/drunomics/nuxt-component-preview/releases/tag/1.0.0-a... is released with the new feature.
I started working on this and pushed some WIP code, it's a first draft.
Disclosure & Note: I used AI to help creating this!
Created MR. For now it is hard-coded to canvas field. I opened the todo for making a better, general solution over at 📌 Auto-configure best settings based upon field type Active .
Ready. This requires some changes in extjs also. Creating issue.
fago → changed the visibility of the branch 3549952-canvas-integration-add to hidden.
I had to implement slot support in the component-preview helper module of Nuxt first. I made sure it works with nested components also:
https://github.com/drunomics/nuxt-component-preview/releases/tag/1.0.0-a...
Given that, I got it working, even with nested slots. It works with latest custom elements 3.x and https://github.com/drunomics/nuxt-component-preview/releases/tag/1.0.0-a... or later.
However, during testing I found one issue. The canvas overlay for interacting with components is not calculating the right position when having JS-rendered layouts, it seems to be fail to find the right position. As workaround elements can be dragged and selected from the left-array sidebar meanwhile. This is probably something we need to look into with canvas folks.
alpha8 tagged - it's compatible with alpha8. see https://github.com/drunomics/nuxt-component-preview/releases/tag/1.0.0-a...
There is also a test fail, but it's unrelated and also appeared in 3.x. Merging and opening a bug ticket for the test fail.
I already have things + tests working. It needs an updated nuxt-component-preview module. It won't be compatible to alpha6, api changed.
So tagging a new alpha first.
I've added a test-case demonstrating the issue, as I described it in #79. (branch 3050261-test-case-2 in issue fork)
ok, good progress was made on the custom_elements module side of things. It has now a working nuxt-preview-provider integration. The glue to select the provider and configure the base-url from Lupus-Decoupled is missing still, thus a few steps are required for testing the setup.
Test setup
- Install custom_elements module with latest 3.x dev version
- Install drupal canvas with draft MR / fork from ✨ Add a generic JavaScriptComponentSource Active
- Install drupal and enable both modules
- Launch a test-frontend with https://github.com/drunomics/nuxt-component-preview configured. The easiest way to do that is to clone https://github.com/drunomics/nuxt-component-preview and to use its dev-playground. However, you need to make sure CORS is configured correctly. For that, copy the example CORS config from the README into nuxt.config.ts - with your drupal base URL
- Register components in Drupal canvas. For now this is a manual process, run
drush php-eval '\Drupal\canvas\Plugin\Canvas\ComponentSource\JavaScriptComponent::createTestComponentEntities();'
to create test-config that matches the test-components available in the playground. - Create a new canvas page and test it. During editing nuxt-components should render correctly.
Thanks, yes. The idea is to move this to a contrib for now, so we can move on quicker. It could be integrated in canvas at later state then.
As you can see in the draft MR, I'd envision this is a directly usable component source plugin, not a base class. It would be generic as it would work with any external JS bundles provided.
ok, merged it, so we can move on more easily and have green tests again. Let's open new issues for all remarks found.
I don't thank that Drupal caches the base-path, that's determined fresh from each request.
Based on what I've seen and read, I think what is happening is this:
1. cache clear - no pages cached
2. Frontpage is one of the first hits at /index.php - with some server configuration Drupal generates now wrong links which point to /index.php/
instead of /page
3. Users and bots are following wrong linkes to /index.php/
. Drupal wrongly generates the page with basePath /index.php
4. Wrongly generated caches end up in page cache.
oh, before I forget, the AI disclosure: I used AI to help me developing this!
the cors config is actually working fine, so that's the way to go. I documented it as part of https://github.com/drunomics/nuxt-component-preview also.
This has a green build + it works together with github.com/drunomics/nuxt-component-preview fine!
Thanks, moving on then!
The phpstan issues are already resolved in the next issue, so let'S get it addressed as part of it then.
Also running into this issue. Seems like 🐛 Fix mismatch of $base_url with Symfony request object Needs work could help solving this.
Testing it with a custom service like this:
custom_elements.preview_provider.nuxt:
class: Drupal\custom_elements\Plugin\CustomElementsPreviewProvider\NuxtPreviewProvider
factory: ['@plugin.manager.custom_elements_preview_provider', 'createInstance']
arguments: ['nuxt']
calls:
- [setBaseUrl, ['%custom_elements.nuxt_preview.base_url%']]
tags:
- { name: custom_elements.preview_provider, priority: 100 }
This PR is ready to review, testing it atm together with ✨ Add a generic JavaScriptComponentSource Active
I commented at the MR. The docs are at https://git.drupalcode.org/project/custom_elements/-/blob/6dd32efafbc7ad... - does that explain it to you?
Additional background can be found at comment #5. I wrapped my head around it, but could not find a simpler way to achieve it. It might be more logical if lupus-decoupled would provide the plugin-manager, but then I think it's useful to have them in custom_elements module, so it could be used easily by any kind of decoupled-setup with having to use lupus-decoupled. One would have to provide necessary glue to provide the base-url, but that could even be done in a custom services.yml in a project.
ok, finally I've got something which I think is ready to go. It lays the ground-work, such that we can add individual preview-providers in separate issues now. For now, this will be 📌 Add a Nuxt preview-provider Active with hopefully some next.js preview-provider following soon.
The architecture with both tagged-services and plugins might seems a bit overwhelming, but in the end we need both. I tried documenting it nice in code/interface to clarify what is the purpose of each.
So the preview-provider usually would happen automatically per-request, however there are use-cases where we want to select them:
1) Lupus Decoupled wants to add UI to allow the site-builder to choose from the list of given preview-providers, then apply them to the request.
2) Custom Elements might want to expose a UI during ce-entitity-display configuration that allows multiple preview-providers (e.g. Markup, Nuxt, ..). But that's in the end just two: the markup preview + what is the default for the request.
So it seems, all what is necessary for 2), is the service for preview providers + the way the resolve the right preview provider by request. We just make sure that preview-provider plugins implement PluginInspectionInterface - such that custom elements has the metadata about the resolved provider.
for 1), Lupus Decoupled wants to configure its own preview-provider-service based upon the configured plugin, so it can be resolved.
ok, given berdir's feedback I tried to skip setAbsolute() for external URLs. That makes the code simpler, what's nice!
However, I don't see test-coverage being added for relative URLs to be returned correctly. This is missing here.
> Are we certain that all clients out there really conform to that?
I did some research on that and did not find any report of issues with relative location urls. This post suggests that even 2012, before it became official standard, browsers supported it already: https://webmasters.stackexchange.com/questions/31274/what-are-the-conseq.... Since it's even out in the standard now >10 years I think it's safe to use!
Thanks! Here my thoughts:
- First off, I think we must be careful with naming of classes here to clarify what kind of preview we are talking about. At 📌 Add API to preview custom elements in Drupal Active I'd like to introduce $custom_element->preview() for being to render the custom-elment with JS in the drupal UI. So this is, more a preview of "entity-ce-display config" right
- Then, how would you envision the UI for that? I think that is important to clarify everything else. Imo, it would go on the "Manage CE" screen, e.g. have a preview-area in the bottom, similar like Views does it?
- Last, I'd was thinking of this as entity-level preview, not field level. E.g. use the pre-existing API to generate preview/test-data of entity field items (as done by layout builder).
As pointed out in #140 ✨ Allow profiles to define a base/parent profile [continue of #1356276] Needs work , recipes are there now. Thus, I think this (long-time open) issue is a too massive change that's not worth it doing any more, i.e. I think it would make sense to mark it as won't fix.
hm, I did not mean to change assignee, it seems I cannot undo that change. :-/
Thanks for the comment!
code components are JS-powered, so JavaScriptComponent (config entity type) + JsComponent (source) make sense
I'd not say the naming of "JS-Component" makes no sense at all, I can see the reasoning behind the name. I'd argue it's not optimal though, since it gives a generic name to a specific component, but imo that's picky and not the main reason for the rename.
I think the main point here is consistency, be able to find the code for what you see in the UI, have a label and machine name of a plugin that fits to each other.
this is not unlike Node, which also has a very different human-readable label: "content items"
I see, true. Still, I'd argue that's a quite sub-optimal, historically grown situation and nothing that we should be striving for.
But that BC break would be more doable given the inifinitely more painful BC break imposed upon us by the rename.
Yes, I fully see and understand the issue.
My thinking was, it's still in alpha and we could still do that kind of breakage before beta, now. However, I understand there is also friction in the development involved, so if it's decided to keep it for causing less breakage, I'd understand also.
Implemented the suggest rename (first phase without config entity only for now).
Ops, this only applies to Drupal <11.2. This is actually resolved in Drupal 11.2. See 📌 [PP-1] Document the deprecation of simpleConfigUpdate on config entities, and new setProperties config action Postponed
What's left is only fixing https://www.drupal.org/project/distributions_recipes/issues/3515735 📌 [PP-1] Document the deprecation of simpleConfigUpdate on config entities, and new setProperties config action Postponed
Started implementing a draft version. So far, javascript components can be created and registered, no rendering is implemented yet.
For serving the "Canvas developer use-case" better it might actually be useful to optionally load a group of JS-bundles from a configurable, absolute URLs. That way, it could become possible to define such JavaScript components without having to write custom Drupal code. That way this could be used together with any kind decoupled JS frontend where the JavaScript part could be implemented.
Found some working config:
vite: {
server: {
cors: {
origin: ['http://xb-dev.ddev.site'],
},
},
},
nitro: {
routeRules: {
'/nuxt-component-preview/entry.js': {
cors: true,
headers: {
'Access-Control-Allow-Origin': 'http://xb-dev.ddev.site',
'Access-Control-Allow-Methods': 'GET',
},
},
'/_nuxt/**': {
cors: true,
headers: {
'Access-Control-Allow-Origin': 'http://xb-dev.ddev.site',
'Access-Control-Allow-Methods': 'GET',
},
},
},
},
So it seems CORS is really the simpler variant. Let's better clearly document CORS has to be setup correctly + make it easy in our nuxt component-preview helper.
Thx! I reviewed all the changes and they are generally solid. I'd say we can move on with releasing this and create individual issues for everything that might pop up during testing. Also moving this to version 3.x, since this going to be released as 3.x.
I think the following should be in a change record, to ease upgrading to the new major:
- config-key change
- environment variable change (OHDEAR_SITE_ID -> OHDEAR_MONITOR_ID)
Maybe a note that dependencies changed also?
Thinking about this topic a 2nd time, the CORS route seems to be better. The proxy-setup is rather complicated and might confuse people much more than a few CORS-requests running in the browser.
For nuxt dev mode, I got CORS working immediately.
For nuxt dev mode the following addition to nuxt.config.ts did the trick (replace origin URL with your drupal base-url obviously):
vite: {
server: {
cors: {
origin: ['http://xb-dev.ddev.site', 'https://xb-dev.ddev.site'],
},
},
},
When running the regular nuxt server, a different configuration is going to be required.
However, a straight-forward configuration as following did not do the trick. Investigating.
nitro: {
cors: {
origin: ['http://xb-dev.ddev.site', 'https://xb-dev.ddev.site'],
methods: ['GET'],
},
},