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

Custom Actions #1452

Closed
VinayaSathyanarayana opened this issue Jul 30, 2019 · 8 comments
Closed

Custom Actions #1452

VinayaSathyanarayana opened this issue Jul 30, 2019 · 8 comments

Comments

@VinayaSathyanarayana
Copy link
Contributor

VinayaSathyanarayana commented Jul 30, 2019

Feature request

There are a number of different use-cases that keep surfacing in relation to the extensibility of the AdminUI. I think these can be roughly grouped into 3 related features:

  • Custom Actions
  • Admin UI theming and interface extensibility
  • Custom 'views' for fields

To give a brief description each:

Custom Actions

This refers to actions a user wants to preform on an item or multiple items in a list. Some examples include:

  • Export to PDF/Excel/JSON
  • Duplicate an item
  • Archive an item

This involves a custom function which has access to the list data. We could potentially implement a standard UI for custom actions and completely separate the customisation of the UI into another feature.

Admin UI Hooks: theming and interface extensibility

This refers to the ability to customise parts of the UI. Some examples include:

  • Adding custom CSS
  • Overriding the render method of particular UI elements (to hide or replace them)
  • Adding additional custom UI elements to particular sections of the UI
  • Modifying data such as a message show to the user

Custom UI element will need to have the ability to call actions (including custom actions) on click or other events.

Custom "Views" for fields

This refers to using an alternative UI elements for a field. This could potentially use the same mechanism as other UI extensibility but may also have unique requirements or an alternative API for fields may just offer a better UX/DX. Examples include:

  • Displaying read-only text without an input field
  • Alternative options for displaying and selecting relationships
  • A gallery view for images
  • Displaying of computed fields?
  • Formatting of JSON stored as sting or generally improving display of other complex data stored as text

Describe the solution you'd like

To keep things as simple as possible and leverage familiarity with the existing APIs I'd like to make use the same API as list & field hooks: https://v5.keystonejs.com/guides/hooks

I think we can do everything we want for custom actions and Admin UI extensions with the same API the only difference is it's defined on the AdminUI.

UI hooks should get access to a bunch of common props such as the current path and auth information so that they can be applied to specific pages or user conditions.

const adminApp = new AdminUIApp({
  hooks: {

    /* These hooks are actions that run at a specific time but do not return anything */
    pageLoad: ({path}) => { 
       /* runs on every adminUI page */ 
       if(location === `/admin/users/`) {
         /* runs on every users list page */ 
       } 
    }
    /* These hooks are filters that replace existing data */
    footer: ({UIObject}) => {
      /* Some actions get access to UI objects with methods that allow us to register new items */ 
      /* Methods for UI objects don't need to be defined in this spec. They can be unique but should follow some conventions */ 

      /* Register an action and render with standard UI */ 
      UIObject.registerAction('Archive', (props) => { /* do something */ });

      /* Register a footer action and render with a custom UI */ 
      UIObject.registerAction(
         'Duplicate', 
         (props) => { /* do something */ },
         ({action}) => <a onClick={action}>Dupe</a>;
      );
      return UIObject;
    },
    header: ({listName, UIObject}) => {
      UIObject.registerBodyClass('custom-class-name')
      UIObject.registerStylesheet('path/to/styles.css')
      return UIObject;
    }
  }
})

To finish this we need to define at least a few UI initial hooks to implement along with a few UI objects and their methods.

It might be useful to establish some guidelines around when we should have a new hook and when we should have a new UI Object. For example you could have a stylesheets hook or you could have a header hook with a more complex UI object with more methods. I think the balance is somewhere in the middle.

Custom "Views" for fields

Custom views may or may not need a different API. We could consider registering a hook for each field element. I think the advantage of registering them on the AdminUI is we don't need to try and pass functions through the webpack barrie. You can just register a hook and use a function to add a UI element.

const adminApp = new AdminUIApp({
  hooks: {
    textField: ({page, UIObject}) => {
      if(page === `/admin/users` && UIObject.key === 'name' ) {
        // Replace the render method of the UIObject (field)
        UIObject.render( ({props}) => <p>{props.value}</p> ) 
      }
      return UIObject
    },
    relationshipField: ({page, UIObject}) => { /* override a specific relationship field */  }
  }
})

I'm open to an alternative here but I think maybe it should be hooks all the way down.

Additional context

This issues was updated to capture input from:

I'd like to close the related issues in favour of this if we can agree that this is the general solution.

@jesstelford
Copy link
Contributor

We don't have any dates or solid roadmaps lined up yet, but this is definitely a feature we're keen to have.

It goes along with all the other ways to "add custom functionality to the Admin UI", such as custom pages, custom headers & footers, rearranging the way items are rendered, etc.

@VinayaSathyanarayana
Copy link
Contributor Author

Thanks @jesstelford

@gautamsi
Copy link
Member

Possible addition to this is logic for default Search box on list page. I have a situation which i can handle by either adding a custom search box for a list or extend the existing search box and change query accordingly.

@MadeByMike
Copy link
Contributor

MadeByMike commented Oct 28, 2019

No addition to the logic is needed:

const adminApp = new AdminUIApp({
  hooks: {
    searchBox: ({search}) => {
      search.modifyQuery(initialQuery => {
        /* stuff here */
      }); 
      return search
    },
  }
})

@MadeByMike
Copy link
Contributor

In each case, the hook returns a bunch of common props including the page, authentication object and any other contextual props that may be used as conditionals to decide if a hook should be applied. In addition to that, it can expose a UI object that has a bunch of unique methods to modify the object. One example might be to modify the search query.

@VinayaSathyanarayana
Copy link
Contributor Author

We should also consider Dynamic Field Notes keystonejs/keystone-classic#4787

@stale
Copy link

stale bot commented Feb 26, 2020

It looks like there hasn't been any activity here in over 6 months. Sorry about that! We've flagged this issue for special attention. It wil be manually reviewed by maintainers, not automatically closed. If you have any additional information please leave us a comment. It really helps! Thank you for you contribution. :)

@MadeByMike
Copy link
Contributor

We've decided that we don't want an imperative API for registering action and have made steps towards adminUI hooks with the ability to override components. The listed usecase should be possible with this in combination with custom GraphQL queries:

  • Export to PDF/Excel/JSON
  • Duplicate an item
  • Archive an item

Discussion on this topic now lives here: https://github.com/keystonejs/keystone/discussions/3027

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

No branches or pull requests

4 participants