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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

NavigationElement #641

Open
gnapse opened this issue Mar 23, 2022 · 2 comments
Open

NavigationElement #641

gnapse opened this issue Mar 23, 2022 · 2 comments

Comments

@gnapse
Copy link
Contributor

gnapse commented Mar 23, 2022

馃殌 Feature request

There's a common pattern in a few places of our products: a list element that acts as a link, but also has some optional information and/or contextual action buttons on its right side. A few examples:

  • List elements in Twist's list of threads or list of conversations (chat rooms):
    image

  • Navigation elements in sidebars of both Twist and Todoist:

    image image

I wonder if we can come up with a component that would serve as a generic blueprint to implement this pattern once and for all.

Motivation

I've had this in mind for a long time now. But what really made me request it now is that this pattern is not trivial to achieve in a way that's done right. In particular, the fact that it is a link that visually has some other interactive elements "inside" it (the optional action buttons to the right). But link elements are not allowed to have interactive elements inside it. So what really needs to be done is to create a markup that's correct, with the link and action buttons being "siblings", but visually place everything so that it gives the impression that the link contains everything.

See for instance https://github.com/Doist/twist-web/pull/4658, where @pauloslund had to refactor our Twist thread links to be like this, because having the buttons inside the link was having a really adverse consequence for Firefox users.

Requirements

These navigation elements have a few things in common:

  • They encode in them different states, such as idle vs. active (active can be hovered and/or focused)
  • They show a main content aligned to the left
  • They optionally show an icon to the left of the main content
  • They optionally show some secondary content aligned to the right
  • They optionally show some actionable elements to the right when hovered or focused. These are shown in a way that replaces on screen the regular secondary content described on the previous point
  • They should allow for some flexibility in size (e.g. sidebar nav items are smaller than thread links)
  • The "icon area" on the left of the main content may need to enforce consistent spacing. It may also need to be flexible enough to allow for both a single icon (e.g. as in sidebar nav items)

This list above may not be exhaustive yet.

Example

An example for how this feature would be used.

  1. It could use a props-based approach to provide all the possible slots of content that can be customized:

    <NavigationElement
        icon={<ThreadIcon />}
        mainContent={
            <Box>
                <Text>{thread.title}</Text>
                <Text tone="secondary" size="caption">{thread.snippet}</Text>
            </Box>
        }
        secondaryContent={
            <ThreadChannelInfo />
        }
        actions={
            <ThreadActionButtons />
        }
    />
  2. Alternatively, it could provide "sub-components" to fill in the slots, similarly to how our modal works:

    <NavigationElement>
        <NavigationElementContent icon={<ThreadIcon />}>
            <Text>{thread.title}</Text>
            <Text tone="secondary" size="caption">{thread.snippet}</Text>
        </NavigationElementContent>
        <NavigationElementSecondaryContent>
            <ThreadChannelInfo />
        </NavigationElementSecondaryContent>
        <NavigationElementActions>
            <ThreadActionButtons />
        </NavigationElementActions>
    </NavigationElement>
  3. An alternative that applies to both cases above is how to deal with the duality of content shown on the right. That is, the alternation between showing some content on the right, vs. action buttons when hovered, replacing the regular content on the right. Instead of providing them both as in the previous two examples, we could consider giving the consumer of this component the ability to decide, via a render prop:

    <NavigationContent
        {...props}
        secondaryContent={
            isActive =>
                isActive ? <ThreadActionButtons /> : <ThreadChannelInfo />}
    />
    
    // or with the components approach
    <NavigationElement>
        {...}
        <NavigationElementSecondaryContent>
            {isActive =>
                isActive ? <ThreadActionButtons /> : <ThreadChannelInfo />}
        </NavigationElementSecondaryContent>
    </NavigationElement>
  4. Some other alternative? Let me know.

Please, keep in mind that the above suggestions are just that, and that someone actually jumping in on implementing this should weight the pros and cons, and also consider all this against the actual practicalities of making this component work in all the situations that this pattern appears in our apps. Names and specifics of components need not be exactly like any of the above examples. We can discuss specifics below, as this progresses towards execution.

@henningmu
Copy link
Contributor

Option 2 sounds good and looks inline with our other components (modal, tabs, etc.), but, as you mentioned, I'd leave this to the one implementing this component.


Do you think the NavigationElement could become a bit of catch-all component with tons of nuanced use cases? For example, would a TaskListItem in Todoist use the NavigationElement as well? Or should we restrict the use cases from the get go to start icon, primary and secondary text, and end actions?

@gnapse
Copy link
Contributor Author

gnapse commented Mar 24, 2022

Do you think the NavigationElement could become a bit of catch-all component with tons of nuanced use cases? For example, would a TaskListItem in Todoist use the NavigationElement as well? Or should we restrict the use cases from the get go to start icon, primary and secondary text, and end actions?

Indeed, something to consider. I also acknowledge that trying to achieve too much with it could make it cumbersome. At the very least, I think that threre's definitely potential to make one that would power all the sidebar navigation items. Whether we can expand it to twist-like thread link elements (with a different left-side icon area, and a different border when focused), it may prove to be too much for a single component. And Todoist's task list elements are even more different. I would not try to make it support that (TD task list elements even have a different layout, with the action elements overlapping the content with some gradient transparency, the overflow menu button lies outside the list element boundaries, and they also have a drag-and-drop handle also floating outside the box boundaries).

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

No branches or pull requests

2 participants