silverstripe / silverstripe-admin

Silverstripe Admin Component
BSD 3-Clause "New" or "Revised" License
25 stars 93 forks source link

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

Open tractorcow opened 6 years ago

tractorcow commented 6 years ago

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

Background

At the moment, the status quo is:

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):

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 commented 6 years ago

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 commented 6 years ago

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?