-
Notifications
You must be signed in to change notification settings - Fork 147
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
docs: new TopNav API #2386
docs: new TopNav API #2386
Conversation
|
✅ PR title follows Conventional Commits specification. |
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit cf4f0a4:
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit c35ab88:
|
{({ items, moreItems }) => { | ||
return ( | ||
<> | ||
<TabNavItems> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good API 🫡
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks to @chaitanyadeorukhkar for the proposal. 🚀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice annotations 🫡
|
||
```jsx | ||
<TabNav | ||
items={[ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to both pass the items here and then also specific which component to use to render this data? 🤔
Since the structure of the TabNav is well defined, with TabsNavItems and Menu, we should be able to do away with one of these 2 things right?
Defining both seems a little verbose and repetitive. Let me know what do you think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need to both pass the items here and then also specific which component to use to render this data? 🤔
Why was this prop-based API chosen over the old compound component API?
Since the structure of the TabNav is well defined, with TabsNavItems and Menu, we should be able to do away with one of these 2 things right?
Tho structure is well defined, the items needs to be dynamically move around depending on screen size, TopNav's interaction requires us to control what items are shown horizontally and what items goes inside the "more" menu dynamically depending on the screen width.
And since we want to keep the flexibility of letting consumers choose what/how things gets rendered inside the More menu we need to accept the data item object instead of using compound API.
For example:
An item say "Payroll" doesn't have "description" prop when rendered in the horizontal manner but while inside the "More" menu it's whole layout changes and it also needs to render that "description", where will we get this "description" from tho, we get it form the items
array.
Check the alternative APIs section for more detailed problem statement and approaches we tried out.
Screen.Recording.2024-10-29.at.11.57.54.AM.mov
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks nice and clean sir 🫡
I've dropped a few more comments regarding this, I'm not able to currently see a case where we need to provide users the flexibility of what needs to be rendered
</Menu> | ||
</> | ||
); | ||
}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@anuraghazra how does the TopNav actions behave on mobile this is the behavior that we are expecting in the connected dashboard OneNav? Is there a way to acheive this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay so it's on the consumers to define what happens on mobile?
Also what about the search on mobile? Are the consumers supposed to handle that as well?
The behaviour across viewports is well defined in the design, can we not leverage that as source of truth and handle the mobile variant accordingly from inside the component, without having the consumers define the behaviour?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay so it's on the consumers to define what happens on mobile?
Yes.
Also what about the search on mobile? Are the consumers supposed to handle that as well?
The search input itself can be easily added as part of the TopNav or even in the page itself since we support slots.
As to what happens on clicking the search it's product side decision, whether they want to open a modal/bottomsheet etc. This can be achieved on consumer end with existing components.
|
||
Mobile Navigation Bar: | ||
|
||
<img width="50%" src="./top-nav-mobile-example.png" alt="TopNav Mobile Example" /> | ||
<img width="50%" src="./top-nav-mobile-example-new.png" alt="TopNav Mobile Example" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is TopNavBrand hidden on mobile? Also does TopNavContent handle the required alignment/spacing on mobile?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given most of the things are not even present in mobile for topnav it's recommended use Adaptive layout.
isMobile
? <TopNav>mobile items/icons/avatar</TopNav>
: <TopNav>
<TopNavBrand />
<TopNavContent />
<TopNavActions />
</TopNav>
We've added examples and code in storybook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you elaborate a little by what do you mean by use Adaptive layout, not able to understand it in the context on TopNav
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example of Adaptive Layout:
On Desktop: "show Modal"
On Mobile: "show BottomSheet"
Example of traditional responsiveness
On Desktop: "Show Modal"
On Mobile: "Shrink Modal's size"
In an adaptive layout or component you sometimes totally change what components are rendered and cater to specific UX requirements (eg: BottomSheet on mobile, modal on desktop)
```jsx | ||
<TabNav | ||
items={[ | ||
{ href: '/home', title: 'Home' }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how does one redirect to an external link?
Also is react-route a peer depenceny?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No peer dep, TabNavItem behave as normal links only. We have href/target etc props.
TabNavItem can be enhanced to work with react router, we have examples in storybook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We ensure we accept props that react router requires, eg as
href
, onClick
etc.
const TabNavItemLink = React.forwardRef<
HTMLAnchorElement,
Omit<TabNavItemProps, 'as'> & {
activeOnLinks?: string[];
}
>((props, ref) => {
const location = useLocation();
return (
<TabNavItem
ref={ref}
{...props}
as={Link}
isActive={isItemActive(location, { href: props.href, activeOnLinks: props.activeOnLinks })}
/>
);
});
{({ items, overflowingItems }) => { | ||
return ( | ||
<> | ||
<TabNavItems> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this wrapper component for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, was wondering why we added an additional component since it wasn't present before right?
Will this be a blade component alongside TabNavItem which will be exposed to the consumer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As i said
it's width we also determine the overflowing behaviour.
This is the container that holds your items and when it's width shrinks we automatically detect it via the ResizeObserver.
@@ -264,6 +473,298 @@ const Dashboard = () => { | |||
- The navigation bar will have `role=navigation` to indicate that it is a navigation landmark. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we have an announcement for screen readers when a TabNav item is clicked?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will have proper announcements on focus.
Clicking generally doesn't require announcements, because on route change the router itself will announce any page state changes.
**Pros:** | ||
1. Users have easy access to the overflowingItems via props | ||
2. Intuitive and faimilar API, similar to Table API | ||
3. Users have the flexibility to render TabNavItemLinks or any other component. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we want to provide users with the flexibility to render anything? I think it is safe to assume that TabNav will always have a TabNavItem and a Menu right?
The Atlassian DS follows only the prop based approach which makes it less verbose overall -> https://atlassian.design/components/atlassian-navigation/examples
I think we can exercise some governanace here to follow consistency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we want to provide users with the flexibility to render anything? I think it is safe to assume that TabNav will always have a TabNavItem and a Menu right?
It's about what's inside the Menu. We don't know.
And we had a design discussion also that even pingal is not sure what might change in future.
The layout could change (eg: two column layout inside Menu or a product specific column layout, like 1 column for payments, 1 column for payroll), menu's header/footer etc can change.
We don't want to keep a hard dependency here because otherwise there will be a lot of blockers on product side if something changes and will introduce breaking change.
Having this flexible means product can decide what/how and where to put things.
href="/payments" | ||
icon={MagicCheckoutIcon} | ||
// this meta data will be passed to `items` array | ||
overflowMetadata={{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here we don't need this meta data right? Only thing different is the description and we can pass a description to all TabNavItemLinks and only show it once it is in more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only an alternative API we explored and had discussions around, having description as a different prop also is considered in other APIs.
This is similar to Dropdown where on initial render we loop over all the JSX elements build a list and store it and from that list we render. | ||
|
||
**Not leaning towards this API mainly because:** | ||
1. JSX as Data gets very [complicated](https://github.com/razorpay/blade/blob/2e4b2cb309cdf3e87982cda9252e9ce97747c475/packages/blade/src/components/ActionList/actionListUtils.ts#L137-L159) as seen on Dropdown, and most of the time breaks composition, like it looks like the component is flexible and composable but in reality it isn’t. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you shed some more light on why the component is not flexible and composable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TLDR;
In dropdown the problem is we do composition via React.Children.map & check for the item's componentIds and if we don't find it we throw error, problem with this is it sort of breaks composability where now if you wrap the Dropdown items in any other wrapper component it will stop working.
**Not leaning towards this API mainly because:** | ||
1. JSX as Data gets very [complicated](https://github.com/razorpay/blade/blob/2e4b2cb309cdf3e87982cda9252e9ce97747c475/packages/blade/src/components/ActionList/actionListUtils.ts#L137-L159) as seen on Dropdown, and most of the time breaks composition, like it looks like the component is flexible and composable but in reality it isn’t. | ||
2. Sets a odd expectation, where users need to pass the description/icon etc props to TabNavItemLink which that component may not even use but rather they are treated as “data” attributes for the overflowingItems | ||
3. We need to get the overflowingItems from somewhere, it will either be as a controlled state or we may need to introduce a render prop inside <TabNav> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
didn't quite get this, if JSX is action as data can pass one more prop to mark an item as initially overflowing and then inside the component construct the data that we are passing through props right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't quite understood what you didn't understood. :p
View Formatted Doc