-
Notifications
You must be signed in to change notification settings - Fork 32
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# RFC x: Roadmap for new StreamField development | ||
|
||
* RFC: x | ||
* Author: Matthew Westcott | ||
* Created: 2020-08-28 | ||
* Last Modified: 2020-08-28 | ||
|
||
## Abstract | ||
|
||
This RFC presents a roadmap for future development of StreamField, based on surveys, interviews and general feedback from Wagtail developers. | ||
|
||
## Specification | ||
|
||
Based on the Wagtail developer survey and follow-up interviews, we've found that developers have the following feature requests for StreamField: | ||
|
||
### UX and performance improvements to StreamField within the Wagtail admin | ||
|
||
These include a number of features already implemented in third-party code - the ability to duplicate blocks (currently implemented in react-streamfield) and to split rich text blocks into multiple blocks (currently implemented as a customisation by NYPR). | ||
|
||
Features such as these are currently blocked from being added to Wagtail core as they rely on undocumented properties of the client-side code which cannot be generalised to all block types, or cannot be guaranteed to remain stable in future Wagtail releases. | ||
|
||
To illustrate: the rich client-side UX as seen in react-streamfield is built upon the ability to perform fundamental operations such as: "create a new instance of a StreamField block, populated with a given value". However, a key principle of StreamField is that any Django form field may be used as a block - and Django form fields do not guarantee anything about the client-side behaviour of a field (except to the extent that it needs to be able to produce an HTML form submission that the Django-side component can make sense of). Assumptions that might seem safe, such as a Django form field corresponding to a single HTML input element, cannot be relied upon - and without that, there is no fully robust way to implement those fundamental client-side operations. | ||
|
||
(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. | ||
|
||
**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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the case of file upload fields, the 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 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. |
||
|
||
### Easier ways to manipulate StreamField data within Python code | ||
|
||
Wagtail does not provide any formal APIs for manipulating StreamField data in place. The officially recommended way to update StreamField data is to construct a new data structure replicating the previous value but with the desired changes applied, and assign that back to the StreamField; for complex nested structures, this is difficult and in certain edge cases, impossible. Workarounds include building the StreamField value in JSON format (which is a less human-friendly representation and adds an extra data-conversion round trip) or accessing StreamField internals (such as the StreamValue.stream_data property) directly, which is often poorly understood (leading to logic errors, especially when previewing) and liable to change across Wagtail releases. | ||
|
||
**Proposed development:** Extend the existing StreamValue class (currently designed to be an immutable value object) with list-like methods that allow it to be modified in-place. Initial work on this has been started, at: https://github.com/wagtail/wagtail/pull/2886 | ||
|
||
### Ability to build your own StreamField-like editing interfaces outside of the Wagtail admin and have those interoperate with Wagtail's StreamField | ||
|
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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. |
||
|
||
**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.) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
|
||
## 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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:
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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" :)