Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC 59: Roadmap for new StreamField development #59

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

gasman
Copy link
Contributor

@gasman gasman commented Aug 28, 2020

No description provided.

@gasman
Copy link
Contributor Author

gasman commented Sep 9, 2020

Have created a proof-of-concept of what a Javascript API for form field manipulation might look like... https://github.com/gasman/telepath-poc

Copy link
Member

@thibaudcolas thibaudcolas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exciting :) I had a brief look at https://github.com/gasman/telepath-poc as well, but too early to provide feedback on this.

I’m wondering whether web components could be interesting to consider as part of this API – it’s not super clear to me how interesting they are for data encapsulation (getState, setState), but they provide APIs to define components and encapsulate each component’s implementations that could be relevant. This might be too modern for the client-side Django widget landscape though.


This limitation also causes performance issues in the current implementation of StreamField - since blocks populated with a given value cannot be created client-side, displaying the edit view for an existing page requires these to be rendered up-front on the server, often generating a lot of redundant HTML.

**Proposed development:** Define and implement a common interface for accessing and manipulating Django form fields on the front end. This will most likely take the form of a set of Javascript 'adapter' objects, one per field type, implementing operations on that field type such as "create a new instance of this field populated with the given value", and "extract the JSON-serialisable value from an instance of this field". Developers implementing new field types for use within StreamField will need to provide such an adapter for that field; for conventional form fields consisting of a single HTML input, this will be a straightforward and well-documented task.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there fields that are supported in the current StreamField implementation, and would no longer work over this paradigm? For example file uploads?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side question – are there any examples of APIs similar to this in the Django world (say Saleor?), or in other frameworks? I don’t have the best sense of likely pitfalls.

Copy link
Contributor

@zerolab zerolab Sep 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/mirumee/saleor-dashboard is probably the best place to check how Saleor does it. The Dashboard is the store admin with various forms.

But we should probably contact the team and get some input from them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of file upload fields, the setState method would have no effect, as browsers don't let you populate them from HTML or Javascript. However, the end result of that is the same as it would be for any other implementation: the field would be left empty on page load when editing an existing page, and on duplicating a block.

In theory you could embed the file data into JSON as a base-64 encoded string, but it's probably worth thinking about a less hacky solution... in the ordinary page-edit view, it probably makes most sense to stick to the Django-ish approach where a StreamField named body would generate a distinct form field named body-file-1 to be picked up by the server-side code. For auto-saving or a DRF JSON API, you'd probably want to come up with something different, so it's best that the JS API doesn't force you down one path. Maybe an additional method that returns the file as a File or FileList object for the calling code to use as it sees fit, then...

I can't think of any other field types that would need special treatment - if there's any way to interface with a field through JS, then it ought to be possible to write that code inside a Telepath adapter object, so I don't think this pattern constrains us in any way.


(StreamField itself is an extreme example of this assumption being broken - it presents itself to other Django-side code as one form field, but may consist of many HTML form elements. This in itself is not a showstopper for react-streamfield, since there's no real prospect of a developer trying to embed StreamField inside a block inside StreamField. However, it does imply that any future development of a similar complexity to StreamField will not be usable within react-streamfield; and conversely, any future development that attempts to make the same assumptions as react-streamfield is liable to fail when presented with a StreamField. In other words: the "eat your own dogfood" test is a meaningful one to apply here!)

This limitation also causes performance issues in the current implementation of StreamField - since blocks populated with a given value cannot be created client-side, displaying the edit view for an existing page requires these to be rendered up-front on the server, often generating a lot of redundant HTML.
Copy link
Member

@thibaudcolas thibaudcolas Sep 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not clear to me whether the work proposed below will address these, but might just be me failing to see it. Does the work below include serializing the whole StreamField’s value as a JSON structure server-side, for it to then be rendered client-side with the Django form fields adapters? I think the below implies that, but worth confirming.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on our discussion, the answer is "yes" :)

  • Serialising server-side, injecting extra data as needed (for example, de-referencing page ids to edit/live URLs + title)
  • Adapters then know how to render the serialised data into a form client-side
  • There are also further opportunities for optimisation (which might also be possible currently, but would make more sense as part of this type of rework) – having a global (page-level) registry of adapters/field templates, rather than having this repeated at different depths in the StreamField tree structure.


Developers frequently request the ability to use StreamField on a site front-end, to allow users to contribute to a site without having a Wagtail editor account. Given the amount of work involved in making StreamField work in the relatively controlled environment of the Wagtail admin, and ensuring that all possible combinations of elements are styled appropriately, we do not feel that it's currently feasible to build a StreamField component for site front-ends with the full generality of the Wagtail admin's StreamField. However, a special-purpose StreamField interface built for one specific page type with a fixed set of blocks can take various shortcuts: it does not need to interoperate with Django's form framework, will most likely not need to handle arbitrary nesting of blocks and has fewer styling combinations to test.

As this would be site-specific bespoke code, written in the site implementer's preferred front-end framework, Wagtail itself would not provide any tools for building this; however, Wagtail does need to provide a way for the resulting data to be submitted back so that a page can be created.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of a REST API more than a reusable StreamField component implementation – but it feels like it wouldn’t be too much work to provide both? (a React implementation at least). It’s good design for a StreamField component to have explicit, direct dependencies, and be renderable outside of Wagtail when those dependencies are met.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's probably an 80/20 rule at work here - 80% of StreamField users only use 20% of the functionality, but it's never the same 20%.

I certainly wouldn't be against a full-featured standalone StreamField implementation (and having one would improve the code quality of Wagtail a lot - the "better chuck _editor_js.html into your template if you want to do anything useful" thing bothers me immensely...) - but I think that building it would be a much bigger job than it looks at first glance. Taking something like ImageChooserBlock - there's the immediate job of untangling the dependencies of image_chooser_modal, modal_workflow and so on, but there's also the more general issue of what it implies to give a front-end user access to the image library, and whether different projects need different approaches for how to lock permissions down... at that point, it feels more pragmatic to say "if you're part of the 20% that actually need an image chooser in your bespoke front-end StreamField UI, then go ahead and build it with whatever business rules make sense to you".

I also get the sense that the CSS would be as much of a nightmare as the Javascript side, but you have a better perspective on that than I do :-)


## Further development

Items 1 and 3 combined could potentially serve as the basis for an auto-save feature in the Wagtail page editor: the Javascript adapter system (item 1) would provide a way to extract the contents of the active edit view as JSON, and a background AJAX request could then post this JSON to a new REST API endpoint (item 3) that saves the data to a draft revision.
Copy link
Member

@thibaudcolas thibaudcolas Sep 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d suggest fleshing out this section further, to sanity-check that the developments above do indeed allow for those features – feels like this is our chance to get the foundations right so we get to build those exciting features, that will make Wagtail feel more modern.

Specific features that could be interesting to validate:

  • Auto-save of page edits in the admin as outlined here
  • Inline editing of pages (directly on the site’s pages, outside of the admin). I think the save mechanism is similar to auto-save(?), but it might make us think differently on how to package the client-side APIs and UI components.
  • Instant preview in the CMS – I think this is already buildable with the current implementations, but worth checking whether any of the above makes it easier – for example a "render preview" REST API.
  • Collaborative editing – auto-save problems taken further, making sure the page data is also serialisable in a format that’s collaboration-friendly ("diffs over websockets") like CRDTs or operational transforms.
  • StreamField copy-paste – Clipboard-serialisable StreamField content for cross-pages (or cross-sites!) copy-paste.

Copy link
Contributor

@zerolab zerolab Sep 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://wagtailcms.slack.com/archives/C014L7KJH3N/p1599936875057300 has a demo of "Inline editing of pages (directly on the site’s pages, outside of the admin)". https://gitlab.com/kocherga/core/-/commit/ef1638ff0ede9305462a9096bd486e53ae6fbf73 for the code implementing that via GraphQL. It is quite specific to the project, but showw appetite for it


As this would be site-specific bespoke code, written in the site implementer's preferred front-end framework, Wagtail itself would not provide any tools for building this; however, Wagtail does need to provide a way for the resulting data to be submitted back so that a page can be created.

**Proposed development:** Implement a REST API endpoint to allow creating pages from posted JSON data, where any StreamFields are also represented in JSON format. (It is expected that JSON will be a more convenient format for front-end developers to work with, rather than the HTML form submission currently used by the Wagtail admin.)
Copy link
Contributor

@kaedroho kaedroho Sep 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also allow this to be used for other snippets and modeladmin as well?

Copy link
Contributor Author

@gasman gasman Sep 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, why not... presumably implementors would register an API endpoint for each distinct model, just as they currently do for the read API, so effectively all that means is that we need to provide a base viewset class that doesn't have the page publishing workflow hardcoded into it.

@kaedroho kaedroho changed the title Roadmap for new StreamField development RFC 59: Roadmap for new StreamField development Nov 19, 2020

Developers frequently request the ability to use StreamField on a site front-end, to allow users to contribute to a site without having a Wagtail editor account. Given the amount of work involved in making StreamField work in the relatively controlled environment of the Wagtail admin, and ensuring that all possible combinations of elements are styled appropriately, we do not feel that it's currently feasible to build a StreamField component for site front-ends with the full generality of the Wagtail admin's StreamField. However, a special-purpose StreamField interface built for one specific page type with a fixed set of blocks can take various shortcuts: it does not need to interoperate with Django's form framework, will most likely not need to handle arbitrary nesting of blocks and has fewer styling combinations to test.

As this would be site-specific bespoke code, written in the site implementer's preferred front-end framework, Wagtail itself would not provide any tools for building this; however, Wagtail does need to provide a way for the resulting data to be submitted back so that a page can be created.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this would be site-specific bespoke code, written in the site implementer's preferred front-end framework, Wagtail itself would not provide any tools for building this; however, Wagtail does need to provide a way for the resulting data to be submitted back so that a page can be created.

for this, it can't be done by any built-in modifications/implementations, as you already said (preferred front-end framework) and also what block types the site builders decide to make them available for site visitors, therefore, the best way wagtail community and core team can offer, is to provide few use case samples, and let the site-builders dig deeper and innovate as the develop their projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants