Skip to content

[WHIT-2788] Configurable document form objects#10972

Merged
eYinka merged 4 commits into
mainfrom
configurable-document-form-objects--all-in-one
Jan 12, 2026
Merged

[WHIT-2788] Configurable document form objects#10972
eYinka merged 4 commits into
mainfrom
configurable-document-form-objects--all-in-one

Conversation

@ChrisBAshton
Copy link
Copy Markdown
Contributor

@ChrisBAshton ChrisBAshton commented Jan 6, 2026

What

This PR refactors the schema design for config-driven document types by separating concerns that were previously conflated under edit_screens. We now:

  • removed settings.edit_screens, in favour of a new top-level forms hash. It describes things at the UI level: which fields appear on which forms.
  • adds new top-level presenters field, describing which field values get sent to Publishing API (and how to map them)
  • removes schema.properties in favour of schema.attributes: effectively a slimmed down version of the former.
  • schema.validations remains unchanged, but we no longer have 'nested' validation (e.g. schema.properties.duration.validations) - the 'nested' validation has moved up into the schema.validations (in this example, schema.validations.duration). This isn't represented in the overview below as we only had one example and it's more of a side-effect than a deliberate change.

Overview of schema changes:

"schema": {
-  "properties": {
-    "body": {
-      "title": "Body",
-      "type": "string",
-      "format": "govspeak"
-    }
+  "attributes": {
+    "body": {
+      "type": "string",
+    }
+  }
}
+ "forms": {
+  "documents": {
+    "fields": {
+      "body": {
+        "title": "Body",
+        "description": "The main content of the page",
+        "block": "govspeak"
+      }
+    }
+  }
+ },
+ "presenters": {
+   "publishing_api": {
+    "body": "govspeak"
+  }
+}
"settings": {
-  "edit_screens": {
-   "document": ["body"]
- }

Why

Bunching up properties into 'edit screens' was a good first step in #10786, allowing us to update different block content fields in isolation on separate tabs. But it was too restrictive: we had no way of denoting which fields should be 'grouped' (e.g. start & end date fields for an overall 'duration') and also we were having to wrestle with wanting to present the fields in one way but present them differently when presenting to Publishing API. The previous properties and edit_screens concepts were conflating too much.

The refactoring of the schemas we've done here should make it quicker and easier to iterate document types in future.

Manual Testing

We have checked on integration to be sure nothing is broken so far, in terms of usability and integrity of the block content data. Details of how we carried out the test can be found in here.

Jira: https://gov-uk.atlassian.net/browse/WHIT-2788


⚠️ This repo is Continuously Deployed: make sure you follow the guidance ⚠️

This application is owned by the Whitehall Experience team. Please let us know in #govuk-whitehall-experience-tech when you raise any PRs.

Follow these steps if you are doing a Rails upgrade.

@ChrisBAshton ChrisBAshton force-pushed the configurable-document-form-objects--all-in-one branch 8 times, most recently from d205372 to c41b916 Compare January 6, 2026 16:13
# end
# Is this test needed anymore? We think now that we're using flattened data structures,
# the above tests cover all addition/overwriting/deletion scenarios, and this 'nesting'
# test is now redundant.
Copy link
Copy Markdown
Contributor Author

@ChrisBAshton ChrisBAshton Jan 7, 2026

Choose a reason for hiding this comment

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

I was thinking about this yesterday - even though we only have flattened top level properties now, we need to make sure that the values are retained, even if the values are nested structures. So make sure we have a test for something like

"foo" => { "bar" => { "baz" => 123 } }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe this has been sorted in:

assert_equal({
"info" => "updated info",
"details" => "updated details",
"bool" => false,
}, bc["meta"].to_h)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

LGTM but can we remove or update the comment above the test?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's done. Thanks!

Copy link
Copy Markdown
Contributor

@lauraghiorghisor-tw lauraghiorghisor-tw Jan 12, 2026

Choose a reason for hiding this comment

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

I do wonder about this, sorry - the types we can now store in our blocks are https://github.com/alphagov/whitehall/pull/10972/changes#diff-ae39aa868855f099aa58be0acefa4cd2ab924fb72aafba6e9201d87a9b6dd9c9 - so they can never be an object. Do I misunderstand?
image

Your merge test doesn't have a problem with that structure but I think it's misleading to have it here as it's no longer relevant.

Copy link
Copy Markdown
Contributor

@lauraghiorghisor-tw lauraghiorghisor-tw Jan 12, 2026

Choose a reason for hiding this comment

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

If we must still support arrays and the like, then I think we need to re-introduce the object type and revisit that deleted code from block_content and add relevant tests there rather than in the merge test.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think @GDSNewt and I can reintroduce that code if necessary (while working on social media links for topical events)? Given the nesting isn't needed for our currently supported formats and we've already tested this simplified schema on Integration.

We're going to have to solve how to do 'arrays' in config-driven world anyway, and will tackle questions around validating the structure of each array element as part of that work.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am in agreement. SHIP IT (well, after I've had a look at the last commit).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I've had a look, final LGTM ⛴️

@eYinka eYinka force-pushed the configurable-document-form-objects--all-in-one branch from e640534 to dc80288 Compare January 7, 2026 18:50
@ChrisBAshton ChrisBAshton force-pushed the configurable-document-form-objects--all-in-one branch 3 times, most recently from 76a00d5 to 25addb1 Compare January 8, 2026 09:26
Comment thread test/unit/app/models/configurable_associations/factory_test.rb Outdated
@eYinka eYinka force-pushed the configurable-document-form-objects--all-in-one branch 6 times, most recently from 56d815a to f4b7d53 Compare January 9, 2026 11:28
Copy link
Copy Markdown
Contributor

@lauraghiorghisor-tw lauraghiorghisor-tw left a comment

Choose a reason for hiding this comment

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

Had a read-through, LGTM and seems all that's needed was carried over nicely 😎 I did check tests in detail 👍

Comment thread app/models/configurable_document_type.rb Outdated
Comment thread test/unit/app/models/configurable_document_type_test.rb Outdated
Comment thread test/unit/app/models/standard_edition_test.rb
Comment thread public/configurable-document-type.schema.json Outdated
Comment thread public/configurable-document-type.schema.json
Comment thread public/configurable-document-type.schema.json Outdated
@eYinka eYinka force-pushed the configurable-document-form-objects--all-in-one branch from f4b7d53 to 77a7ec9 Compare January 9, 2026 16:46
@eYinka eYinka marked this pull request as ready for review January 9, 2026 16:51
@eYinka eYinka changed the title [WHIT-2788] Configurable document form objects WIP [WHIT-2788] Configurable document form objects Jan 9, 2026
@lauraghiorghisor-tw lauraghiorghisor-tw force-pushed the configurable-document-form-objects--all-in-one branch from 77a7ec9 to ffbf9cf Compare January 12, 2026 10:19
@lauraghiorghisor-tw
Copy link
Copy Markdown
Contributor

I just approved but realised one little omission: the publishing_api_payload methods of all the content blocks can now be removed. Can that please be committed? Can be a separate commit.

@eYinka eYinka force-pushed the configurable-document-form-objects--all-in-one branch from ffbf9cf to 92988c6 Compare January 12, 2026 11:01
lauraghiorghisor-tw and others added 3 commits January 12, 2026 11:15
This now replaces the need for all the `publishing_api_payload` methods defined on all the config driven blocks. There is no need to define a payload behaviour for the `default_object` block, as we always send a flat details hash to publishing API. Only "leaf" blocks will be included in the payload.

All the tests covering the blocks' `publishing_api_payload` behaviour have been ported over.
This commit refactors the schema design for config-driven document types by separating concerns that were previously conflated under `edit_screens`.

We have:
1. Introduced a new way to compute standard edition presenter payload. We want to specify which attributes we're going to be including in the presenter, separately from defining the model attributes, or the forms to render.
2. Introduced a separate `schema` section for the model. The schema now includes `attributes` with a `type`, rather than a hash of `properties`. This will help us separate the model schema from what we want to render, and send to publishing API, respectively. This section also includes `validations`. This structure is now flat, and it can only include "leaf" blocks that the model knows about and validates.
3. The rendered `forms` get their own section in the schema, separate from model and presenters. We now "skip" nested objects when rendering; this means that whereas before the form name would have looked like `edition[block_content][wrapper_object][leaf_block]`, it now looks like `edition[block_content][leaf_block]`.

We have converted all StandardEdition schemas to use the new schema format and modified code accordingly, in particular some rendering logic. Standard edition feature tests have been updated to cover the new schema.

Technical notes:
- Introduced an alternative configurable block factory builder. It builds uniquely named blocks and abandons the `type > format` nested structure we had in the `blocks` method. Since we now have an easy flat structure of computing the schema/publishing api payload, the `wrapper` object has been dropped in the new method `build_block` as it will be no longer needed.
- The way we compose the path when rendering child blocks in the new schema style has also changed.

Notes on schema validations:
- Ensured the schema of schemas `public/configurable-document-type.schema.json` carries the updated structure and put necessary validations in place.
- Removed references to `properties`.
- Required new properties such as `presenters` and `forms`.
- Removed the 'object' from the schema's possible `attribute` types, as we only ever expect a flat structure now.

Required labels change:
- Rendering the "required" labels require accessing the `validations`. Validations now live in a different segment of the schema; previously we were fetching them straight from the top-level `schema`; now the "schema" passed for rendering a block only contains `fields` with title/block/description. This was fixed by passing the array of required fields through any `default_object` block, so it's available down the tree. We compute the required fields values in the `ConfigurableDocumentType` model's`required_attributes` method.
- The required attributes are now being passed for the images and the translations edit form.

Note:
In the future, it's worth renaming the "schema" property in the configurable document schema to something like "model" as it makes more semantic sense and could potentially reduce confusion.

Co-authored-by:       Olayinka Oladele <olayinka.oladele@digital.cabinet-office.gov.uk>
Co-authored-by:       Jamie Stamp <jamie.stamp@digital.cabinet-office.gov.uk>
Co-authored-by:       Chris Ashton <christopher.ashton@digital.cabinet-office.gov.uk>
Co-authored-by:       Laura Ghiorghisor <laura.ghiorghisor@digital.cabinet-office.gov.uk>
Co-authored-by:       Ryan Brown <ryan.brown@digital.cabinet-office.gov.uk>
We tried testing setting an object value under the new 'flattened
attributes' structure, and the object was only ever being set as
`{}`. This is because we had special case handling for type "object"
which is no longer necessary - we blindly set the value to the
object, no type casting necessary.

We also no longer have the concept of a nested 'validations' or nested 'schema'
property on a nested attribute. All validations are declared at
the root level now and can incorporate multiple attributes, as
we're doing with topical event date validation:
https://github.com/alphagov/whitehall/blob/25addb1ce63d150731560b7a99efc89e731ecf21/app/models/configurable_document_types/topical_event.json#L60-L62
@lauraghiorghisor-tw lauraghiorghisor-tw force-pushed the configurable-document-form-objects--all-in-one branch from 92988c6 to 7a53e43 Compare January 12, 2026 11:15
…ck models

Based on the new schema refactor to use "forms" property, we have now moved publishing api presenting to our new block abstraction method so this is no longer needed.

See `app/presenters/publishing_api/payload_builder/block_content.rb`
@eYinka
Copy link
Copy Markdown
Contributor

eYinka commented Jan 12, 2026

I just approved but realised one little omission: the publishing_api_payload methods of all the content blocks can now be removed. Can that please be committed? Can be a separate commit.

Good spot @lauraghiorghisor-tw. I've just added d5e7f9a to clean that up.

@eYinka eYinka merged commit 8982257 into main Jan 12, 2026
25 checks passed
@eYinka eYinka deleted the configurable-document-form-objects--all-in-one branch January 12, 2026 14:43
eYinka added a commit that referenced this pull request Jan 12, 2026
…chema

This was omitted in the [forms schema refactor PR](#10972). We no longer make use of that property in the schemas.
eYinka added a commit that referenced this pull request Jan 12, 2026
…chema

This was omitted in the [forms schema refactor PR](#10972). We no longer make use of `edit_screens` property in the schemas.
eYinka added a commit that referenced this pull request Jan 13, 2026
This commit adds an ADR that explains why we have adopted the new forms structure
in configurable documents schema and the benefit for us, especially with validating nested blocks.

PR: #10972
eYinka added a commit that referenced this pull request Jan 13, 2026
This commit adds an ADR that explains why we have adopted the new forms structure
in configurable documents schema and the benefit for us, especially with validating nested blocks.

PR: #10972
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants