Page MenuHomePhabricator

Decision on how UI schema abstraction should work
Closed, ResolvedPublic

Description

Background

CommunityConfiguration client library "json-form" generates a generic form based on json-schemadefinitions. On the high level it renders a Codex field for each property found in the given schema. In some situations there's a need to do some visual customisation of the form in a particular provider. For instance hiding a particular property, displaying a property with a non-standard control, customize the form layout or grouping properties.

In order to meet the initial release of the extension deadlines some visualization customisations have been introduce in the extension ad-hoc, mixing validation and presenation concerns. An example of this mixing of concerns is the "control" keyword used for rendering MediaWiki specific controls via references, (T357718).
The parent form specification task had a proposal to avoid this problem: using a UI Schema. However the definition was quite vague:

The concept is borrowed from jsonforms/uischema and consists of a JSON definition file which describes the controls, layout and rules of a form.

As an example:

schema.json
{
	"$schema": "https://json-schema.org/draft-04/schema#",
	"$id": "http://example.com/communityconfiguration/help-panel/1.0.0",
	"type": "object",
	"properties": {
		"GEHelpPanelAskMentor": {
			"type": "boolean",
			"description": "When using the help panel's question-asking functionality, post on the mentor's talk page instead of on the help desk page.",
			"default": true
		},
		"GEHelpPanelHelpDeskPostOnTop": {
			"type": "boolean",
			"description": "Whether to post new questions on the top of the help desk. Default is to post on the bottom (like section=new does). Only affects wikitext pages.",
			"default": true
		},
		"GEHelpPanelExcludedNamespaces": {
			"type": "array",
			"items": {
				"type": "number"
			}
		},
		"GEHelpPanelReadingModeNamespaces": {
			"type": "array",
			"items": {
				"type": "number"
			}
		},
		"GEHelpPanelSearchNamespaces": {
			"type": "array",
			"items": {
				"type": "number"
			}
		}
	},
	"required": [
		"GEHelpPanelAskMentor",
		"GEHelpPanelHelpDeskPostOnTop",
		"GEHelpPanelExcludedNamespaces",
		"GEHelpPanelReadingModeNamespaces",
		"GEHelpPanelSearchNamespaces"
	],
	"additionalProperties": false
}
uischema.json
{
    "type": "VerticalLayout",
    "elements": [
        {
            "type": "Group",
            "label": "Simple group",
            "elements": [
                {
                    "type": "Control",
                    "scope": "#/properties/GEHelpPanelAskMentor"
                },
                {
                    "type": "Control",
                    "scope": "#/properties/GEHelpPanelHelpDeskPostOnTop"
                }
            ]
        },
        {
            "type": "Group",
            "label": "Complex group",
            "elements": [
                {
                    "type": "Control",
                    "scope": "#/properties/GEHelpPanelExcludedNamespaces"
                }
            ]
        }
    ]
}

The UI schema does not need to be necessarily defined in JSON it could also be done in PHP. If no UI schema is provided CC2.0 can fallback to a "vanilla" set of controls and some default settings for form displaying.

Possible use cases of UI schema

Related Objects

Event Timeline

KStoller-WMF renamed this task from Decide if a UI schema abstraction is required to Decision on how UI schema abstraction should work.Oct 17 2024, 4:25 PM
KStoller-WMF moved this task from Needs Discussion to Triaged on the Growth-Team board.
Michael updated the task description. (Show Details)

Change #1311915 had a related patch set uploaded (by Michael Große; author: Michael Große):

[mediawiki/extensions/CommunityConfiguration@master] [PoC] Add optional UI schema for form layout

https://gerrit.wikimedia.org/r/1311915

Proposal for introducing a UISchema to CommunityConfiguration with supporting custom controls as the first step

Both from the Abstract Wikipedia team and from the Reader Experience team, there have been recent requests to be able to use custom form controls for some use-case or other.

CommunityConfiguration is closely inspired in its architecture by JSON Froms (https://jsonforms.io/). The approach that JSON Forms uses for such customization is a UISchema (https://jsonforms.io/docs/uischema/). CommunityConfiguration is using/creating a very simple UISchema internally in the frontend part of the code, which essentially only is "Render a control for all fields in the data schema, one after the other."

The proper way to allow consumers of CommunityConfiguration to gain more control over how the form is rendered is to allow them to provide their own UISchema.

To unblock the teams asking for custom controls, the first iteration of UISchema in CommunityConfiguration should be built to allow extensions exactly that.

The proposed implementation should have the following properties:

  • backwards compatibility: existing schemas without an associated UISchema continue behaving in exactly the same way
  • graceful degradation: if a control defined by a UISchema is not available, CommunityConfiguration uses its internal logic to pick a suitable control
  • separation of concerns: the new UISchema should be exclusively for presentational aspects, everything around validation must continue to live in the data-schema

Specifically, building on the example of T424097:

We want to get to a state where existing data-schema would have an optional reference to the associated UISchema, like so:

class SuggestedFunctionsSchema extends JsonSchema {

	public const UI_SCHEMA = SuggestedFunctionsUISchema::class;

	public const SuggestedFunctionsEnabled = [
		self::TYPE => self::TYPE_BOOLEAN,
		self::DEFAULT => false,
	];

	public const SuggestedFunctions = [
		self::TYPE => self::TYPE_ARRAY,
		self::ITEMS => [
			self::TYPE => self::TYPE_STRING,
			self::PATTERN => '^Z[1-9]\\d*$',
		],
		self::MAX_ITEMS => 5,
		self::DEFAULT => [ 'Z20756', 'Z18428' ],
	];

	public const Example_String = [
		self::TYPE => self::TYPE_STRING,
		self::DEFAULT => '',
		self::MAX_LENGTH => 50,
	];
}

With the referenced UISchema looking something like this:

SuggestedFunctionsUISchema.php
class SuggestedFunctionsUISchema extends UISchema {

      public const ROOT = [
          self::ELEMENTS => [
              '#/properties/SuggestedFunctionsEnabled', 
              [
                  self::TYPE => self::TYPE_CONTROL,
                  self::SCOPE => '#/properties/SuggestedFunctions',
                  self::CONTROL => 'WikiLambda.FunctionSelector',
                  self::OPTIONS => [
                      // presentation-only knobs the control understands,
                      // e.g. 'showZidChips' => true
                  ],
                  self:MESSAGES => [
                      // the (extra?) message keys needed for this control
                  ],
              ],
              '#/properties/Example_String',
          ],
      ];
  }

Note that self::CONTROL should usually be a plain string and not a PHP FQDN. A PHP FQDN seems to mix abstractions and has details like namespaces leak.

The control itself would be registered as an attribute, similar to what is sketched out in T424097.

CommunityConfiguration needs to be extended so that likely GenericFormEditorCapability understands and scans the associated UISchemas and adds the requested js modules (and i18n messages, probably those should also live in the extension.json section).

The client-side registry needs to load the custom controls. This is probably the least well-defined part. Currently, this is a closed system that just loads all the handlers CommmunityConfiguration has defined and that needs to change. TBD on how exactly that would look, whether via mw.communityConfiguration.registerControl( name, renderer, tester ) or mw.hook( 'CommunityConfiguration_loading_controls') or some other approach. All of that then needs to be wired up.

Open Questions

  • Should the UISchema have to contain all elements, or just the ones it wants to modify?
  • If the UISchema is also defining the order of elements, which makes sense for later iterations that focus on allowing grouping, adding headlines, etc., then it makes sense for an UISchema to typically require all elements. Missing elements would then be just appended at the end of the form, regardless of their position in the data-schema.
  • How specifically would the js code be loaded? This is the issue described above. We need to decide this once, it will be hard to change later.

Notes

  • Especially the first extensions making use of the UISchema will need excellent testing and a mutual CI-dependency with CommunityConfiguration to make sure that future work on CommunityConfigration+UISchema does not silently change how their forms look.

Let's put the proposal under docs/decisions in CommunityConfiguration repo, so it is available for every developer. https://github.com/wikimedia/mediawiki-extensions-CommunityConfiguration/tree/master/docs/decisions

Let's put the proposal under docs/decisions in CommunityConfiguration repo, so it is available for every developer. https://github.com/wikimedia/mediawiki-extensions-CommunityConfiguration/tree/master/docs/decisions

👍

Change #1320200 had a related patch set uploaded (by Michael Große; author: Michael Große):

[mediawiki/extensions/CommunityConfiguration@master] docs: Add ADR about how to introduce a customizable UI Schema

https://gerrit.wikimedia.org/r/1320200

@Michael I see the patch is WIP, so moving to Doing. Happy to CR once finished.

Change #1320200 merged by jenkins-bot:

[mediawiki/extensions/CommunityConfiguration@master] docs: Add ADR about how to introduce a customizable UI Schema

https://gerrit.wikimedia.org/r/1320200