Host Details Page Redesign - Extension points

Hello everyone :wave:

As you may know, we currently redesign the host details page for a better experience.
We are also aware that this page should have opinionated extension support for plugins.
The current design for this page ATM is a body section with cards and a header with general info, actions and tabs, as you can see down below:


I’d like to share with you two directions for an extension mechanism, the first one is already in progress

  1. Tab Extension - Plugins can add tabs on this page with the corresponding content
  2. Card Template - Plugins can add a card template to an existing tab content

Tab Extension

This feature is in progress and allows:

  • Register a react component to be a tab content (core and plugins use the same registering mechanism)
  • Direct navigation to a specific tab via URL (i.e https://foreman-url/experimental/hosts/some-host#Content - navigates to the content tab)
  • Reordering Tabs - a plugin can change the tabs order, for example, looking on the above snapshot, a plugin can bring the Content tab to be located after Details Tab (or any other location) via weights mechanism - the higher the weight - the location will be more to the left

Demo

You can watch a short demonstration by clicking here
You can also try it out - Foreman PR, Katello PR

Implemention

Slot&Fill is used under the hood and I wonder if in this case it would make sense to create a wrapper for it. This is how the registering function looks ATM:

// addGlobalFill(slot-id, fill-id, fill-content, weight)
addGlobalFill('host-details-page-tabs', 'Subscriptions', <SubscriptionTab key="subscription-tab" />, 100);

How about transforming it to this one

// registerTabToHostDetails(tabName, tabContent, tabOrdering)
registerTabToHostDetails( 'Subscriptions', <SubscriptionTab key="subscription-tab" />, 100);)

It’s a small change, but I do think it helps for readability, especially due to the fact that slot and fill is a new and barely used feature, and it may help client-side newcomers understand better.

Card Template

This is still just a thought, due to the current design it makes sense to me that plugins can generate a card by a template which uses a data-driven approach with predefind type (i.e list, table, text etc) - with that we can implement a way to generate such a card in erb views, so plugins that don’t have react can still create some sort of content within the host details page.

# foreman's helper
def register_card_item(tab-name, options)
  # implementation
end
# erb view in a plugin - this adds a list card in that details tab
<%= register_card_item(:details, {
  header: _('Last Tasks'), footer: { content:_('More Tasks'),
  url: path_to_tasks },  type: :list, data: last_tasks_data
 } %>

this would render something like this:
last-tasks

Thanks for reading. I’d like to hear your thoughts and suggestions if you have any about this progress for achieving a better extension mechanism :slight_smile:

5 Likes

Thanks @amirfefer for the writeup, tabs look really nice. It’s good we allow to navigate to a specific tab right away.

Regarding the overall layout of the tab, will that contain cards only? I guess not. But if that was the case, I’m afraid that with customizable weight, once I install a plugin, the cards will shuffle and e.g. Parameters won’t be at the place I was always used to (e.g. top right corner).

For the cards specifically, I think we will need a custom content at some point, but perhaps not from the first version. I guess we could use the text type for that? As long as the text can contain html, that should be fine.

Could you also show an example of how the table data should look like? E.g. for latest tasks, I think we want to display not only the list but at least their status and result, ideally as small icons. Or would that be just an array? If it has too many entries, is the card scrollable?

Should we set fixed height for the card? That would make sure the page is always aligned and we have no gaps on new lines. We could have various sizes, n, 2n etc.

Do we have a mechanism to set the order (like for tabs) or group them somehow? E.g. last config reports may be grouped with last arf reports. Again, probably not needed immediatelly, but something that will be handy later.

Great write-up Amir!

Just one nit: shouldn’t the registr_card_item method call be placed somewhere else then erb template? The erb templates are run only when needed, so ti would be hard to make sure it gets called.

Apart of it, really awesome progress!

1 Like

Awesome work ! Can’t wait to use it when it’s done,
and thanks also for sharing the katello POC :+1:

Each card has a fixed height & width, and also it uses PF4 responsive grid, so larger screens will have more cards in a row. Giving a weight can change the layout order, indeed, but foreman core’s cards are separated from plugins cards, so the Parameters card still will be in the same place. In the long run, I do think we can use a drag&drop mechanism, or custom weights for each user (so the ordering would be customizable to users)

ATM we have key-value pairs with a data-driven approach card component basically for registering cards from rails, however if you register a card component within react (global js file), you can add any react component you wish for. Rendering HTML might be risky and it also can ruin some react bindings (it manipulates the DOM directly).

Each card has a fixed height so any overflow results in rendering a scroll bar. we can create any kind of react content inside a card from a plugin that uses react, so this shouldn’t be a problem (for this case REX and foreman tasks use react)

Yes, we already have that :slight_smile:

We can define a dynamic slot for each group related content - so a plugin can register a dynamic slot, and register grouped content to it.

Some updates:
The tab extension mechanism is merged. Plugins can add dynamic tabs to the new host details page :slight_smile:
You can use this WIP PR as a reference for adding a new tab to this page.

The card extension mechanism is almost done, as you can see here and a simple usage example, which you can find here.

My vision is to create an interface for these kind of extension points, which means you can re-use these mechanisms in other pages. i.e -

import Tabs from '../Tabs';
import PageLayout from '../PageLayout'

 /* extended prop allows this page to be extended
    in the same way of the host details page */

const AnotherPage = () => (
  <PageLayout extended {...otherProps}>
   <Tabs extended />
  </PageLayout >
)
2 Likes