Sidebar Field Extension

Describe the issue:

  • I’m trying to create a field extension where when we reference an object in Hubspot by the object id, we have a sidebar panel added that displays data bout the hubspot record
    • ideally, only if that field has an object id would we render the sidebar panel
  • The idea being that we could have multiple models that each might have a different purpose in referencing an object in hubspot, and each might want to display different fields from the hubspot object
  • A field extension would work great since this allows us to customize the application of the referenced hubspot object to the record type through the field extension configuration options

Issue

  • The concept of this plugin was driven from the existence of the Sidebar Notes plugin, which leverages a non-documented feature of specifying a field extension as asSidebarPanel: true, returned from the manualFieldExtensions hook.
  • However, it appears that this method of rendering in the sidebar, doesn’t allow you to control where in the sidebar the field is rendered (rank, placement)

Questions

  • What is the expected way of implementing a plugin like this?
  • If using the primary way of configuring a sidebar plugin, if i’m in the itemFormSidebarPanels hook, I don’t see how to
    1. easily determine if the sidebar panel should be rendered for a given item type
    2. provide configuration for this particular sidebar panel implementation
    3. dynamically fetch information about the given itemType to determine if it contains a field with a specific name or that might have specific configuration options
  • Would the only method be to configure which model, field, and configuration options to leverage within the plugin configuration screen?

Additional Documentation Issue

  • See the documented ctx methods available to the itemFormSidebarPanels hook. The type I see for ctx in the context of this hook is IntentCtx, which doesn’t include the majority of what is referenced in the documentation here. There are async methods referenced, that IIRC cannot be used because you cannot return a promise from the hook.

Hey @nroth,

First off, the Sidebar Notes plugin is using a very old version of the plugins-sdk, and what looks like deprecated methods/properties. Let’s ignore that for now.

The current, supported method of adding sidebars is, as you say, using the itemFormSidebarPanels hook: Sidebars and sidebar panel | Documentation — DatoCMS. Please make sure you’re using most recent version of the plugins SDK (^2.x.x).

You should be able to:

  • Render an individual panel within the sidebar with itemFormSidebarPanels()
    • You can control placement & rank using the placement & rank parameters — sorry that isn’t clear in the docs, but you can see it in the TypeScript of that hook’s return type.
  • OR render an entire sidebar (take over the entire side, not just a panel) with itemFormSidebars()
  • (You might also consider using a record Outlet instead, which renders at the top of a record instead on the side)

But with any of these, you’re right, the plugin SDK doesn’t allow much in the way of dynamically showing or hiding things. You are limited to using the provided itemType and ctx (which should be of type ItemFormSidebarsCtx or ItemFormSidebarPanelsCtx, not IntentCtx; the normal methods/props should be available there?)

Example:

As for using dynamic data fetched async from the outside, unfortunately you can’t really do that inside the connect() function in main.tsx right now (I wish you could too, and am asking for it internally). But for now, you have to move that part of the logic inside the actual rendered component (which is just standard React or JS).

e.g. the flow would go something like this:

  • Define a sidebar/panel/outlet that gets rendered conditionally, depending on itemType/ID/anything available in the ctx
  • Inside that component, do an async fetch and either display the product data directly, or display one of several possible sub-components to show depending on the conditions met.
  • If there is nothing relevant to show, just show a ā€œNo product info for this product/modelā€ message.

It’s not as clean as being able to dynamically show/hide the thing altogether, I know. Sorry about that!

Sorry, quick note: Seems like rank must be >= 1 to work correctly. Lower values will be higher visually.

A rank of 0 seems bugged right now.

But this would work:

[
	{
		id: 'firstPanel',
		label: 'First panel',
		startOpen: true,
		placement: ['before', 'info'], // Where to place it relative to our default panels
		rank: 1 // Tiebreaker if two panels have the same `placement` value. Must be >= 1. Lower values are visually higher up.
	},
	{
		id: 'secondPanel',
		label: 'Second Panel',
		startOpen: true,
		placement: ['before', 'info'],
		rank: 2
	},
];

I updated the docs to be more explicit about positioning: Sidebars and sidebar panel | Documentation — DatoCMS

Thanks, ok that makes more sense. I was really struggling to understand what was going on with the rank.

Yeah, i was mainly just pointing out how the documentation seems to at least suggest all these methods are available from that ctx type, when they aren’t.

To follow up on how I ended up implementing this configurable sidebar component. The idea was for this kind of data it would be better to render it dynamically than trying to keep the data in sync, given the data we want to display might change over time. So, for a generic hubspot object preview plugin, I wanted to make it as configurable as possible.

Config

On my plugin config page I added a configuration option that queries dato for the available record types, allowing you to select one. This is used to then decide whether or not the sidebar should show up at all for a given record type. Then in the config you can choose a field name that will contain the object id, then we fetch the hubspot object types to choose from, then which fields you want to display from that object type, each contained within a named section. This allows us to render a dynamic preview in the sidebar of any hubspot object type that we might want to reference while authoring content, using a static config that can’t be tied to a particular field as a field extension (like the notes plugin is implemented).

Rendering

Then, once we are in the rendered sidebar component we look for the pre-configured field to contain a value and when it does, we fetch that record from hubspot with the properties we have configured to fetch and render it.

Our sidebar showing the ticket preview and the draft content preview (we use the sidebar panels since we have to leverage multiple panels at the same time and switching is too cumbersome).

Wishlist

Just captured some of the things I came across

  • More dato-styled react components or at least maybe a theme that can be applied to some framework (shadcn, mantine, etc) to provide more components with consistent styling. It seems unreasonable to create all components every needed, so having a theme that applies to a commonly used framework would be a big help.
  • Ability to programmatically open a full sidebar. For a moment I considered supporting opening the full hubspot record in a sidebar if you want to go see the full thing, but didn’t notice a method to do this.
  • Ability to control whether the sidebar panel is expanded or collapsed (rather than just the initial state) or even visible. Ideally I only want it visible and/or expanded when i’m trying to render something. It seems like after I’ve expanded it one time, it stays expanded until i manually collapse it.
  • The data model to configure this plugin ended up being rather complex to support reordering the sections, adding/deleting them, etc. However, if I could have created a dato model and leveraged the built-in UI for configuring the plugin, leveraging the field types, blocks, etc, it would be way less work to defer to that instead. However, the plugin configuration must be static and can’t be fetched dynamically, so doing this would be kind of awkward to manage. So, I had to implement a custom form handler to define my plugin config schema.

Should be fixed now, BTW… rank 0 should work now, as should negative numbers. I updated the docs to better explain all this, with examples: Sidebars and sidebar panel | Documentation — DatoCMS

Maybe I’m misunderstanding you, but you CAN use the ā€œProperties and methods available in every hookā€ there. You just can’t dynamically update the sidebar based on async data (it’s not refreshed on data change).

Or did you mean something else? Was there something in particular (a method or property) not working the way you expect?

I’ll make this into a separate feature request for you so we don’t lose track of this wishlist!