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

Discussion: Extensibility for React GridField (future development) #386

Open
tractorcow opened this issue Dec 18, 2017 · 2 comments
Open

Comments

@tractorcow
Copy link
Contributor

tractorcow commented Dec 18, 2017

Broken out of the larger story related to a react-gridfield component being used within the context of a history viewer silverstripe/silverstripe-versioned#37, I would like to discuss a proposal for a new GridFieldComponent replacement.

Background

At the moment, the status quo is:

  • A developer will declare a "config" which contains a set of component they wish to add to a gridfield, each of which may be provided by third party modules.
  • Optionally, extensions (e.g. lumberjack) can decorate or replace sets of components before they are rendered in the CMS view.
  • Once a gridfield is rendered, each component will be queried to provide replacement HTML / extra features before being sent to the client.
  • Components also act as "url handlers", providing custom endpoints useful for certain actions.

In subsequent 4.x releases we are looking at developing further proof of concepts of a React-based GridField, which will ideally supercede and eventually replace the current php / json GridField.

What I found the most powerful in 3.x's GridField is this extensibility, which meant that a developer could pick and choose features to add in any particular use case, without having to build custom components themselve.

Proposal

My proposal is a re-implementation of a similar interaction of elements, but based on registerable front-end "injection" transformations that can be conditionally applied to any one gridfield instance, similar to form schema transformations.

To give an example of a form schema transformation, this is an existing piece of code taken from asset-admin.

const insertTransform = (form) => {
  const schema = form.getState();
  const overrides = schema.stateOverride && schema.stateOverride.fields;
  const customTitle = (overrides && overrides.length > 0)
    ? i18n._t('AssetAdmin.UPDATE_FILE', 'Update file')
    : i18n._t('AssetAdmin.INSERT_FILE', 'Insert file');

  form.mutateField('action_insert', (field) => ({
    ...field,
    title: customTitle || field.title,
  }));

  return form.getState();
};

This is then applied to a particular form using the below code:

Injector.transform(
    'insert-media-modal',
    (updater) => {
      updater.form.alterSchema('AssetAdmin.EditForm.fileInsertForm', insertTransform);
    }
  );

I imagine something similar to this could be used to group component-transformations within react, applying them conditionally to any gridfield instance based on form schema (rather than hard-coded form names):

  • A set of javascript transformations are registered with injector (e.g. GridField.SortableColumn, GridField.SearchableHeaders). Third party libraries (such as gridfield-extensions module) could register additional transformations.
  • Each component would be backed by a PHP class which is used to generate the necessary formschema for each extension. These classes would potentially also allow for custom php endpoints (although ideally the majority of new endpoints would be graphql based).
  • As with 3.x, a developer would construct a config using a collection of these classes with the necessary options.
  • When the GridField generates its form schema, it will construct a special nested-schema which is built by each of its registered components.

E.g. a sample schema could look like the below:

{
	"name": "BannerImages",
	"component": "GridField",
	"data": {
		"graphQLType": "BannerImage",
		"gridfieldExtensions": [
			{
				"name": "GridField.SortableRows",
				"data": {
					"SortColumn": "Sort"
				}
			},
			{
				"name": "GridField.Searchable",
				"data": {
					"searchableColumns": [
						"Title",
						"Description"
					]
				}
			}
		]
	}
}

Within React, each of the above named transformations (e.g. GridField.SortableRows) would augment the GridField (or possibly of some sub-component, such as a GridFieldHeader, GridFieldCell, etc) being constructed.

@tractorcow
Copy link
Contributor Author

Pinging @unclecheese to provide his react-injector insight to this discussion. :) I've only really presented about half of the solution... I'm hoping you could help me finish it. :)

@flamerohr
Copy link
Contributor

I like the idea, although it does seem close to suggesting that we should be constructing HOCs on render - which is a performance nightmare - by using props to determine which transformations that apply based on props.
I guess this would depend heavily on how those sub-components are constructed and possibly how the transformations work as well.
It could be possible to re-use the mechanism which FieldHolder uses a noHolder prop to then just pass-through the component without doing anything. The subcomponents could have a check against the data.gridFieldExtensions prop to see if the transformation applies or passes through.

The other concern I have is that GridField has methods for augmenting basically EVERYTHING the GridField has, which seems like wishful thinking in the context of React. Particularly this point:

Once a gridfield is rendered, each component will be queried to provide replacement HTML / extra features before being sent to the client.

How do you envision transformations would be able to distinguish between a header sub-component and cell sub-component?

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

No branches or pull requests

3 participants