The driving idea behind most of the dashboard implementation is that we want the frontend to drive the inter-app interactions through API calls, and so the backend mostly acts as a storage and resolution point for the frontend app.
The basic flow for any widget being displayed on the dashboard is as follows:
- User dashboard fetched with list of widgets
- Specific widget displayed in card with name at top
- Widget component resolves widgetDefinition, and passes responsibility down to a specific widget renderer, say SimpleSearch
- SimpleSearch component parses JSON blobs in widgetInstance and widgetDefinition , and uses the information to build a path
- The app makes a call to that endpoint to fetch the data
- The app uses the parsed widgetInstance and widgetDefinition to instruct itself on how to display the incoming data
- Data is rendered onto the widget card
The ethos then for this app overall is that there is a tight set of WidgetTypes, which will be curated and developed very carefully. Since the available WidgetTypes is a closed list, the structure of any Definitions/Instances coming into the system can be absolutely implicitly assumed, allowing frontend code to be built around that.
Once the frontend work is done for a particular widgetType, any new widgetDefinition using that widgetType should just work, complete with:
- A functioning ability to display all configuration options
- A dynamic form capable of putting forward all configuration options to the user
This means that work on a new WidgetType is almost entirely frontend-reliant. Each new WidgetType will require first a JSON schema containing all of the necessary fields and then a new display component, along with any pathBuilders and data massaging components necessary to turn that data into a shape the rendering component needs, as well as a form component capable of building a form for all facets of the WidgetType.
Adding a new definition for an existing type should be very straightforward with minimal investment from the developer. To add a new Definition for an existing Type will require first reading and comprehending the JSON schema for that Type, along with any existing WidgetDefinitions for it, and then writing JSON for your own backend which fits that profile. As stated above, that JSON document should be the beginning and the end of the development work needed to get, say, a SimpleSearch widget for a specific resource to display, and be an option for a user to configure, complete with a fully functional dynamic form to set that widget up.
Open communication of new and interesting use cases and WidgetTypes will be greatly encouraged, although we want to take care that we're targeting reusable and generic enough types to be of value in more than one use case. Eventually there is nothing stopping other frontend modules from providing their own Form and Display components for a given WidgetType, although this is not something we are targeting in the near future for this project.
Specific to the backend, this is intended as both storage for the dashboard, and also to include a "frontend registry". This registry will be able to act as a point of interaction between the frontend and backend, able to return information about differing frontend apps, such as how their url filtering is built, allowing other apps then to make a call to mod-service-interaction and use that information to create pre-filtered links on the fly. Similarly we can use this in the dashboard to offer solutions such as custom display components from apps within tables.
Another use for the backend app is as a place for user-specific information. Similarly to how the dashboard is shown per user, in future this app could be used to store other information a user might want to have specific to themselves.
The frontend app is designed so that we have a consistency between differing widgetTypes, as well as ties to Stripes styling.
The frontend app is built entirely from functional components, and aims to treat Stripes as much as a component library as possible. The hope is that this allows us both future expansibility to information from outside of a users stripes bundle, such as RSS feeds or other things, but also keep to a minimum wrapper on top of react, to ensure that as react changes and evolves this app can keep up with that and be as performant as possible without needing wholesale changes or relying on updates to core stripes components. To that end the frontend does not use SearchAndSort, StripesConnect or MultiColumnList, instead displaying Widgets on a canvas, and using react-query and react-table to fetch and display data.
The way that data is fetched is per widget. This is one of the many reasons for choosing react-query, as a lightweight and powerful tool to allow us to fetch data and tie that to a particular widgetInstance without needing to update the others. To that end each SimpleSearch widget can be refreshed and updated independently of each of the other ones.
Reusability and Extensibility
The dashboard is not tied exclusively to ERM in any way. This way any build can potentially make use of this service/app, and we can attempt to have cohesion and similarity of design no matter which users are using this app, and which module's widgets they're attempting to add to their dashboard.
The overall aim is for a system where new definitions can be added automatically by other modules, requiring absolutely zero work from the ERM team. New Definitions for a Type can be POSTed on the fly, and they will appear as configuration options for users without any development work besides the JSON file.
We hope the backend module mod-service-interaction can become a storage point for certain frontend information, allowing this kind of inter-app interaction as smoothly as possible for both the dashboard, and also any other apps which need this information.
Finally, with all of the components for ui-dashboard we are aiming to be as simple and reusable as possible. A good example is the DragAndDropFieldArray, which has been designed not just for ERM specific use cases, but to work in general anywhere it would be useful to reorder a FieldArray within a FinalForm component. With these we hope to provide both a useful frontend app, but also tools and patterns other frontend developers can use within their own builds.