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

[RFC] New Homepage Technical Design #5251

Closed
BSFishy opened this issue Oct 9, 2023 · 12 comments
Closed

[RFC] New Homepage Technical Design #5251

BSFishy opened this issue Oct 9, 2023 · 12 comments
Labels
os-assistant RFC Substantial changes or new features that require community input to garner consensus.

Comments

@BSFishy
Copy link
Contributor

BSFishy commented Oct 9, 2023

New Homepage Technical Design

Overview

The homepage needs to be redesigned to provide useful links, including favorites, recents, and AI-based smart suggestions for visualizations wrapped up with a modernized visual aesthetic that includes an attractive dark mode that users now prefer.

There are separate implementations of the homepage for 1) the application, 2) a user, and 3) workspaces:

  1. The application: This homepage is configured by the admin and cannot be changed by any user. It is the main homepage when the user is not in a workspace, or has users/user homepages disabled.
  2. The user: This is a homepage specific to each individual user and can be customized freely, without changing any other user’s homepage. An admin can configure the default layout.
  3. Workspaces: This is a homepage individual to each workspace. Users who have access can modify it, and those changes are visible to anyone who views that workspace’s homepage. An admin can configure the default layout.

Screenshot (159)

Section Implementation

Sections are meant to be a small part of the page with a common type of data. For example, this may be useful links to documentation, recently edited visualizations, etc.

Section Type API

The home plugin exposes an API which allows any plugin to register new sections. This API exposes a single function, registerSection, takes in a single object parameter for future compatibility. The API ships initially with limited points of customization, which can be expanded in the future if a proper use-case is found.

The properties of the object are as follows:

  • id (required, type string): This is a unique identifier for the section. It is what the plugin uses in the saved object to differentiate between sections and select the correct one to display.

    The structure of the identifier is [plugin id]:[section slug]. For example, the identifier for a section that contains recent visualizations, from the home plugin, might be home:recentVisualizations. This format isn’t enforced in any way, however sections should be written following this pattern.

  • title (required, type string): This is the title of the section to be displayed in its title bar. This is user-facing and should be internationalized.

    Initially, this property’s type is string, however that may be expanded to React.ReactElement, without breaking, if needed.

  • description (required, type string): This is the description of the section to be displayed below its title. This is user-facing and should be internationalized.

    Initially, this property’s type is string, however that may be expanded to React.ReactElement, without breaking, if needed.

  • documentationHref (optional, type string): This is the URL to navigate to if the user clicks the “Documentation” link below the description. If this property is not provided, the documentation link will not be displayed.

  • categories (required, type ({ data }) => Categories): This is a function that returns categories. The argument is an object, which can be expanded in the future, initially containing data, which is a custom data field that the section can use for whatever it wants. This allows for a higher degree of customizability in section content.

    The data field is data taken directly from the saved object associated with this section instance. More information is in the Configuration section.

    Both the argument and return type of this function are an object. This is meant to increase compatibility with future versions, by allowing new fields to be added without breaking existing code. For example, an id or configuration field could be added to the argument, allowing for more complex category generation code.

Categories return type

The Categories return type is a type that is returned by the categories property of the section registration function. It is an object, which follows the ExclusiveUnion pattern from OUI:

https://github.com/opensearch-project/oui/blob/1fe770cc662888be2d497da7352d6bf6f4f26cc4/src/components/common.ts#L177-L182

Essentially, there are multiple different types of categories, only one type of which is allowed to be used at a time. These types of categories are disambiguated by the type property.

Unified categories

This is a type of categories where all the category blocks are surrounded by a single panel, providing a unified background. This is useful for a long horizontal list of items that aren’t broken up by distinct categories. An example of this is the “Recently accessed“ section in the proposal Github issue.

The properties of this type are:

  • type (required, type ‘unified’): The type disambiguation field
  • categories (required, type React.FunctionComponent<{}>): This is a component which will render in the categories panel. The component does not initially have any properties, however may be extended with properties in the future.
Ordered categories

This is a type of categories where the category blocks are defined in order. The order is not customizable by the home plugin, but the section could implement customizable ordering itself.

The properties of this type are:

  • type (required, type ‘ordered’): The type disambiguation field
  • categories (required, type Array<React.FunctionComponent<{}>>): This is an array of components, which will be rendered as categories in order. The components do not initially have any properties, however may be extended with properties in the future.
Potential future categories

While these are the types of categories we want to support initially, there are a few types of categories that we may want to add in the future:

  • Unordered categories: This is similar to ordered categories, but instead of the order being defined, it is fluid. This allows the user to define the order through customization.

Configuration

To facilitate configuration, homepages utilize saved objects. There are separate saved object types for application, workspace, and user homepages, but they all contain similar structures. This common structure is as follows:

  • id (type string): This is the unique identifier of the saved object. It may be given a special meaning given the type of the homepage.
  • sections (type Array<Section>): This is an ordered list of sections.

Section type

  • type (type string): The type of the section. This is how the renderer know what type of section to render and directly maps to the section ID used in the section type API.
  • data (optional, type unknown): Arbitrary data for use by the section. This type is unknown to allow this data to be anything that the section would like; there is no restriction on the type of data to be provided.

Types of Saved Objects

Each type of homepage gets its own type of saved object:

  • Application homepage: There should only ever be one of these saved objects and mechanisms shall be in place to enforce that.
  • Workspace homepage: The ID of the homepage saved object reflects the unique ID of the workspace.
  • User homepage: The ID of the homepage saved object reflects the unique ID of the user.

User configuration options

Upon initial release, the most flexible way to configure the homepage is through saved object management. Of course, the user can add sections, reorder sections, and dismiss sections from the homepage itself, however beyond that any configuration shall be done through saved object management. For example, if a section allows for arbitrary data, the only way to edit that is through saved object management.

Future configuration options

In the future there may be a number of ways to configure the homepage:

  • Section-level configuration: There may be a built-in mechanism and API to allow for section-level customization. For example, a user may click a “Customize” button on the context menu of the section to pull up a modal to allow switching between recently edited visualizations and recently viewed visualizations.
  • Category-level configuration: There may be a built-in mechanism and API to allow for category-level customization. This would be defined much more by the section rather than the API, to allow for a higher degree of specialized customization.

Front-end

Rendering the homepage is relatively simple. The main task is getting the correct saved object configuration, which is handled by the individual page (application vs user vs workspace homepage) then passed to a unified homepage component.

The overall flow for any given homepage is the same and is as follows:

  1. Retrieve the correct saved object given the current inputs. For example, if it’s a user homepage, get the correct homepage saved object given the current user.
  2. Iterate through the sections and convert them into React.ReactElements
    1. Retrieve the section type from the section type API, given the section’s id.
    2. Convert categories to React.ReactElement
      1. The main thing here is switching off of the categories type and rendering the categories given that type.
    3. Render the section given the rendered categories and other info from the section type.
  3. Render all of the sections onto the page, with all of the other page layout.

Sections

Screenshot (158)

A section is a reusable component to be displayed on the homepage. The component props are as follows:

  • title (required, type string): The title to be displayed at the top of the section. The should come from the configuration and already be internationalized.
  • description (required, type string): The description to be displayed below the title. This should come from the configuration and already be internationalized.
  • documentationHref (optional, type string): The URL to navigate to when the user clicks the “Documentation” link below the description. This should come from the configuration.
  • categories (required, type React.ReactElement): This is the element to be displayed in the categories area.

The component is defined as follows:

  • A header section which is built using a EuiFlexGroup set up as a row. The items are:
    • The title. This comes from the title prop of the component. This flex item is set to grow and take up any extra space.
    • The options menu. This is an icon button that provides the user with actions to move the section up or down, dismiss the section, etc. On initial release, this button is not extendable, however will be extended to use the UI actions API in the future.
    • The collapse button. This is an icon button that collapses the section so that only the header section is visible. Clicking this button when the section is collapse expands it again.
  • A content section which is built using a EuiFlexGroup set up as a row. The items are split up in a 1:3 ratio. The items are:
    • The description part. The content is the description, which comes from the props of the component, the documentation link. The documentation link is only rendered when the documentationHref prop is provided to the component. The text is always “Documentation”, and the EuiLink always has external set to true.
    • The categories. This content comes from the categories prop of the component.

Other considerations

  • Section versioning/migrations: Given that sections have a custom data field it may make sense to provide a mechanism to version and migrate between versions for that data.
  • Section-level customization: It would be nice if sections had the option to be customized. For example, a recent visualizations section might have the option to switch between recently viewed or recently edited visualizations.
@BSFishy BSFishy added the RFC Substantial changes or new features that require community input to garner consensus. label Oct 9, 2023
@BSFishy BSFishy removed the untriaged label Oct 9, 2023
@AMoo-Miki
Copy link
Collaborator

Thanks Matt for the RFC.

  1. I would like to allow users in my organization to customize their homepages but i would like to force a section on top and a section at the bottom of the homepages that they cannot move, edit, or delete. I don't see a way for doing that.
  2. We might want to make sure we capture the creation time, updating user, and update time in the logs or in the saved object itself.
  3. Having the categories be an array of React's functional components:
    a. forces any plugin who would like to interact with the homepage to be written using React; this marginalizes developers who choose not to use React for their plugins, or those who want simple functionality without the headache of maintaining an ecosystem like React's.
    b. allows plugins to run wild with their styling and layouts, possibility breaking the homepage for other plugins and certainly breaking the look and feel.
  4. A disabled plugin's spots on the homepage need to be hidden.
  5. Will there be a saved object that holds a reference to a user's homepage elements saved within other saved objects? Or, will the home renderer just look for a certain type of saved objects and use them for rendering the homepage? How is the ordering determined?
  6. Having interactive components of various plugins on the same browser interface raises a bunch of security concerns. How does the homepage secure the plugins from a rouge or compromised plugin?

@BSFishy
Copy link
Contributor Author

BSFishy commented Oct 10, 2023

  • I would like to allow users in my organization to customize their homepages but i would like to force a section on top and a section at the bottom of the homepages that they cannot move, edit, or delete. I don't see a way for doing that.

  • We might want to make sure we capture the creation time, updating user, and update time in the logs or in the saved object itself.

  • Having the categories be an array of React's functional components:
    a. forces any plugin who would like to interact with the homepage to be written using React; this marginalizes developers who choose not to use React for their plugins, or those who want simple functionality without the headache of maintaining an ecosystem like React's.
    b. allows plugins to run wild with their styling and layouts, possibility breaking the homepage for other plugins and certainly breaking the look and feel.

  • A disabled plugin's spots on the homepage need to be hidden.

  • Will there be a saved object that holds a reference to a user's homepage elements saved within other saved objects? Or, will the home renderer just look for a certain type of saved objects and use them for rendering the homepage? How is the ordering determined?

  • Having interactive components of various plugins on the same browser interface raises a bunch of security concerns. How does the homepage secure the plugins from a rouge or compromised plugin?

  1. That's an interesting idea. Will need to follow up with UX on that.

  2. I'll do some digging into if/how that is done elsewhere in the project and update the design accordingly :)

  3. That's a good point. I designed this in a React-aligned way without even thinking about it, but I agree that if we'll be giving rendering to the plugins, it should be in an agnostic way.

    Beyond that, I've been going back and forth on giving rendering to the plugins in the first place. For example, we could just say that each category is an OUI card and you just need to use the props that it supports. The reason I initially didn't go this route is because of the range of different category types in the proposal. I can identify 5 separate types of categories in the proposal. I think I'll follow up with UX to see if I can't get a complete list of types of categories, then update the design accordingly.

  4. A good point. I'll follow up with UX to see how this should be handled. For example, I install plugin A, which registers a section. I add plugin A's section to my homepage. I disable plugin A. Should the section a) be deleted from the homepage or b) simply be ignored?

  5. Essentially, a user navigates to their homepage. The homepage looks up a user homepage saved object with the user's unique ID as the saved object's ID. If it finds one, use that as the source of truth for data. I'll try to clarify that in the design.

  6. This is a good point. I'll try to update the design to eliminate cases where the plugins are able to arbitrarily render onto the page aligning to what I said in point 3.

@joshuarrrr
Copy link
Member

Thanks Matt, I like the overall approach quite a bit. There are just a couple places where I think we may want a different approach:

Categories

I think the two proposed category types are a little too constrained by the existing mocks. What I take from them is that we want to make sure sections are capable of multiple column-based layouts. Instead of the two types you mention, I think a column-based template or layout system would be preferable. This design doesn't touch much on support for existing embeddables, but I imagine that a section with a 3 column layout could take 3 separate embeddables and could choose whether or not to make some or all of those editable/replaceable/removable.

Props

  1. I suggest that description is an optional prop from the very beginning. I can almost guarantee we'll immediately have a usecase for a section that needs the full page width instead of just 75%.
  2. I think we probably want a more generic name for documentationHref - If it's just a specially treated link, it may be useful for it to link to other things (such as a feedback forum post).

IDs

I don't think the id scheme here is consistent with saved objects in general. What you're talking about our references to other IDs (workspaces, users). This should happen in a dedicated field to store those references, not by trying to match IDs.

@kgcreative
Copy link
Member

I think what we delivered in terms of mocks was scoped to P0 and something that was easily duplicated for the purpose of speed. As I think about this from a framework perspective, i'd like to think about the system and the layout a bit more. To that end, i'd like to see a few of these props be a bit more flexible.

title -> sectionTitle
content -> this can have different content categories, or just be something that any plugin can just return.
description, documentationHref, categories should be props of a specific type of content, but we could have other sections that are simply n panels that accept embeddables

@kgcreative
Copy link
Member

  1. I would like to allow users in my organization to customize their homepages but i would like to force a section on top and a section at the bottom of the homepages that they cannot move, edit, or delete. I don't see a way for doing that.

We specifically didn't include that in the P0, but yes, that's something we should account for in the high level design

@ashwin-pc
Copy link
Member

ashwin-pc commented Oct 11, 2023

Nice! have a few suggestions and questions for the RFC:

Suggestions:

  1. Move some of the visuals further up in the RFC. Its confucing for a new reader what sections and categories are without some visual clues.
  2. A mock of a sample section and category will also help.
  3. The saved object should also store a mapping of the homepage with the type of user/ default homepage type

Questions:

  1. Can you provide a few example sections and categories?
  2. Can 2 or more plugins contribute data to the same section?
  3. How does registration of a section differ from the usage of a section?
  4. Based on the design it looks like a section spans a whole row, is that true?
  5. Why are we going with a new abstraction here instead of using something that already exists like an embeddable. The Embeddable architecture already allows for something very similar and a lot more flexibility

@kgcreative
Copy link
Member

kgcreative commented Oct 11, 2023

2. Can 2 or more plugins contribute data to the same section?

I would say if plugins are contributing embeddables, yes, but that would be to a custom section. Plugins should provide a row.

@kgcreative
Copy link
Member

related: #4966

@ruanyl
Copy link
Member

ruanyl commented Oct 25, 2023

Categories return type

To my understanding so far, define type such as unified or ordered, etc in the returned object sounds a bit unnecessary, seems it's mostly about how to layout the categories? This level of detail might be better handled within the plugin that registers the section, rather than requiring the homepage to understand specific layout types.

For example, for unified type, the document mentioned:

This is useful for a long horizontal list of items that aren’t broken up by distinct categories

In my opinion, this is the rendering logic of the plugin which register the section, the homepage doesn't necessarily to be aware of how to render such categories.

Homepage doesn't need to know that it's unified type so render it as a full width card. Plugins can have more control over how categories are presented. This way, the homepage remains agnostic about layout details, but it will allow for a wider range of category layout options without needing changes to the homepage itself.

Update:
Here is something just came into my mind:

// categories could be something like
[
  [Card1, Card2, Card3], // row 1
  [Card4, Card5], // row 2
  [Card6] // row 3
]

// this translate to UI like:
[...width: 33.3%...], [...width: 33.3%...], [...width: 33.3%...] // row 1
[..........width: 50%.........], [..........width: 50%.........] // row 2
[..........................width: 100%.........................] // row3

This way, the plugins have some level of control over the layout, but from homepage perspective, the overall layout remains unified.

@ruanyl
Copy link
Member

ruanyl commented Oct 25, 2023

documentationHref

I feel having something called documentationHref on section API sounds too specific, I don't know if this will be a common pattern of "Section". I guess in practice, people may want it to be a link, a button, or even an icon.

I may consider simplifying the section API by removing the dedicated documentationHref field. Instead, allow the plugin that registers the section to define the documentation link within the description.

description: () => React.ReactNode

@hdhalter
Copy link

hdhalter commented Mar 5, 2024

@BSFishy Are we implementing some of this in 2.13? If so, can we update the release train value to 2.13? Or is there another issue related to 2.13 that we can attach the doc issue to: opensearch-project/documentation-website#5530. Thanks!

@ashwin-pc
Copy link
Member

Closed by #6065. There is still work to be done to make the new homepage meet its requirements, but that can be tied back to a separate issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
os-assistant RFC Substantial changes or new features that require community input to garner consensus.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants