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

PoC - FDS folder support #4771

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

juanjofgliferay
Copy link
Collaborator

Scope

In this PoC we want to test how to implement a way to navigate within folders inside a FDS, showing its contents. To achieve this, we may want to update the apiURL prop that it is used to retrieve the data for a given FDS.
Also, we need to identify which content is a folder

Concerns

  • We have a weak way to identify what it is a folder. Currently checking the entryClassName of an item to check if it contains *Folder. While this is valid for some Liferay content (Web Content : com.liferay.journal.model.JournalFolder, KnowledgeBase: com.liferay.knowledge.base.model.KBFolder ) it does not work with Objects out of the box.
  • Folder navigation is able to update the apiURL and retrieve new data, but we cannot ensure that the actual table schema matches the upcoming data.
  • Related to the previous one, creation menu could differ from the current to upcoming data.

UI

folder.mp4

: apiURL;

const newApiURL = new URL(fullUrl);
newApiURL.searchParams.set('entryClassNames', item.entryClassName);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updating only the entryClassName but can be anything, in the same way we are doing in loadData helper

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, this function could even be a callback delegated to the FDS caller. FDS should not assume much about how data is structured.

For a PoC this is more than fine though.

In terms of the type of change for apiURL, it's very likely we'll need to modify the filter odata expression. This is an spetialization of the more general function that does an arbitrary modification on the URL, which allows us to make some assumptions:

  • Data about folders is consistently encoded in item field(s). Ideally, but not strictly needed:
    • in one single field,
    • always the same field. This simplifies computation of the relevant odata expression
  • Backend is able to consistently filter out data by such field(s), meaning
    • when such field is used to filter, backend will return elements that are immediate children of the provided "folder"
    • The above happens regardless the type of elements involved, and is compatible with the rest of parameters in the REST request (such as pagination, sort or other odata expressions connected with and operator)
  • Endpoint to read data won't change during component usage because only odata is allowed to change when browsing folders. This is in line with how filters and other parameterization work now.
  • Relevant odata expression can be provided on FDS invocation as part of additionalAPIURLParameters. This is a known requirement
  • We can consider other ways to "delegate" this function to the caller, yet control the way apiURL is mutated. For instance
    • Caller might just return an odata string that FDS appends via and operator
    • Caller could give the parameter that has to be added to the expression, very much like a filter, then FDS would handle the rest

I provide above items just for thought and discussion. We don't need to shape final solution yet, but instead we can prepare FDS to support this feature under such assumptions, fine tuning what we finally surface into component API later on.

@@ -85,6 +86,12 @@ export const INTERNAL_CELL_RENDERERS: Array<IInternalRenderer> = [
name: 'link',
type: 'internal',
},
{
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define renderer as internal for convenience. It could be a customCellRenderer in the CMS project

options: any;
value: string;
}) => {
const isFolder = itemData?.entryClassName.includes('Folder');
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another key aspect of the proposal. I see 3 points of discussion here

First, the isFolder implementation. Similarly to the apiURL mutation function, knowlege to distinguish between a container and a "leaf" item can be delegated to the caller. Of course, for PoC purposes we can provide a default implementation of this function. I don't see a clear way to implement a sensible default, perhaps what you propose is a great best effort way. Still I see the need to open component API to allow alternate implementations to be provided from the outside.

Second, make all this opt in. We need to preserve existing behavior, therefore, enabling container browsing is something that has to be explicitly configured. We need to determine the right way to do this from the perspective of the FDS react component, then translate that to custom//system data sets. We can deal with this later on.

Last, and more important, whether we need a dedicated renderer or not. Using a renderer means allowing folder navigation only in tables, which is fine for now but can be perceived as a bug shortly after we release this.

Opposed to FDS providing isFolder and openFolder functions (let's not consider how configurable can they be) is a clear need, I don't see NavigationLinkRenderer as a must here. Instead, such functions are just helpers for the view to do its job, being a view's responsibility to handle navigation appropriately. This is clearly seen in case we want to provide a custom view renderer.

This does not remove the obligation of FDS to provide folder support for its ootb views, but allows to do it in specific ways.

  • For the case of tables, we can still have this renderer (isFolder shall be hoisted to the top level though). But we might instead enhance ActionLinkRenderer to implement the navigation, and/or wrap the rest of ootb renderers with an onclick function to trigger it. Specific behavior will depend on product management, but also, we need to consider the fact that CMS may want to add their own custom cell renderer
  • Cards can provide an href/onclick handler that would implement navigation
  • Similar for List view, by adding some click handler on the title

onClick={(event) => {
event.preventDefault();

openFolder({item: itemData});
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use the same renderer to navigate to a content (document, media, ...) using navigate utility once they provide an URI for it.

return (
<div
className="col-md-3"
key={
key={index}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not related. Just removing ugly console warnings.

@@ -566,7 +567,10 @@ const FrontendDataSet = ({
}
setDataLoading(false);
}
Liferay.on(EVENTS.OPEN_FOLDER, openFolder);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open an ear just in case we need to listen for external change requests (breadcrumb)

Copy link
Collaborator

@dsanz dsanz Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How event emitter knows which item shall be provided to openFolder() function?

Also, we shall emit this event from openFolder() function so that external parties can know (e.g. update the breadcrumb). A consequence of this is that we must ignore events emitted by ourselves, or emit a different one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I would expect a dumb Breadcrumb component that only displays stuff and provides a callback.

({ active, href, label, onClick, ...otherProps }: IItem)

Whoever configures the breadcrumb (CMS Team) should know which ...otherProps will need when they click on an item so they can update breadcrumbs back and fire the event to FDS with the correct data (folderId x.e.)

@@ -667,6 +668,7 @@ function CellRenderer({
itemId={itemId}
loadData={loadData}
onItemsChange={onItemsChange}
openFolder={openFolder}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of nits here:

  • This function shall be available throughout all visualization modes, probably we can expose it in the context
  • Naming might be a bit more general, folders is just a particular form of containers. I'm thinking about browsing organizations and users, portal instances and sites, or any other containment relationship we might want to support

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openFolder is already available through context, just wanted to keep renderer lean 😃

About naming, we could change to a more generic openContainer or simply browse.

options: any;
value: string;
}) => {
const isFolder = itemData?.entryClassName.includes('Folder');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another key aspect of the proposal. I see 3 points of discussion here

First, the isFolder implementation. Similarly to the apiURL mutation function, knowlege to distinguish between a container and a "leaf" item can be delegated to the caller. Of course, for PoC purposes we can provide a default implementation of this function. I don't see a clear way to implement a sensible default, perhaps what you propose is a great best effort way. Still I see the need to open component API to allow alternate implementations to be provided from the outside.

Second, make all this opt in. We need to preserve existing behavior, therefore, enabling container browsing is something that has to be explicitly configured. We need to determine the right way to do this from the perspective of the FDS react component, then translate that to custom//system data sets. We can deal with this later on.

Last, and more important, whether we need a dedicated renderer or not. Using a renderer means allowing folder navigation only in tables, which is fine for now but can be perceived as a bug shortly after we release this.

Opposed to FDS providing isFolder and openFolder functions (let's not consider how configurable can they be) is a clear need, I don't see NavigationLinkRenderer as a must here. Instead, such functions are just helpers for the view to do its job, being a view's responsibility to handle navigation appropriately. This is clearly seen in case we want to provide a custom view renderer.

This does not remove the obligation of FDS to provide folder support for its ootb views, but allows to do it in specific ways.

  • For the case of tables, we can still have this renderer (isFolder shall be hoisted to the top level though). But we might instead enhance ActionLinkRenderer to implement the navigation, and/or wrap the rest of ootb renderers with an onclick function to trigger it. Specific behavior will depend on product management, but also, we need to consider the fact that CMS may want to add their own custom cell renderer
  • Cards can provide an href/onclick handler that would implement navigation
  • Similar for List view, by adding some click handler on the title

<span className="c-pr-2">
<ClayIcon symbol="folder" />
</span>
)}
Copy link
Collaborator

@dsanz dsanz Mar 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bit signals an interesting requirement: we need to alter the way things are rendered when they are folders.

For table views we have more chances thanks to the renderers. For cards/lists, I don't have anything specific in mind.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cards & List have the symbol property mapped to the schema.
ClayCardWithHorizontal icon defaults to "folder", but there is no design combining cards with folders and documents.

@juanjofgliferay
Copy link
Collaborator Author

@dsanz @markocikos Added a last bonus commit with a first attempt to move FrontendDataSet.js to .tsx .

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

Successfully merging this pull request may close these issues.

2 participants