-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Add Navigation component [Feature branch] #24107
Add Navigation component [Feature branch] #24107
Conversation
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.
Fantastic start, Paul!
Added some comments as well as a link to the component I was working on in the nav repo for reference.
Definitely want to get some tests around this as well, but I think we can tackle in a follow-up since this is already a lot to bite off in a single PR.
import Text from '../text'; | ||
import Item from './item'; | ||
|
||
const Navigation = ( { data, initial } ) => { |
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 initial
the active item? Might be worth renaming for clarity. Some prop types would be a great addition too.
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.
Thats my intention, but agree there is probably a better name. My thinking is that the active menu item will be passed down as a prop from either a router or any other outside logic. As I have it now, this component has state, which we'll need to figure out first (I see you started that discussion below) and hopefully naming of this prop will be more evident.
} | ||
|
||
.components-navigation-title { | ||
// For now |
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.
Will this change?
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.
Yeah, I think there's probably a better way to layout this component than just margin-bottom
to elements. Maybe I should remove as well.
<div className="components-navigation-title"> | ||
<Text variant="title.medium">{ parent.title }</Text> | ||
</div> | ||
<div className="components-navigation-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.
Would love to see this broken down into Menu
components to keep things dry.
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 abstraction to keep in mind once we have the data model and usage sorted out.
.components-navigation-item { | ||
display: flex; | ||
justify-content: space-between; | ||
margin-left: -12px; |
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 feels a little duct-tapey to me. Maybe we could create a scss variable for padding that's used for all items instead of using negative margin.
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.
Yes, agree here. I opted to use just enough CSS to make it look respectable. I'm hoping to concentrate more on the logic at this stage until the markup and componentry are finalised. Maybe its best to remove so it doesn't get left in there?
flex-direction: column; | ||
} | ||
|
||
.components-navigation-item { |
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.
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.
I think we'll need to keep focus outline for keyboard users. At least change the color or something to make it more presentable.
Good point on the long names, that will certainly be an issue.
margin-left: -12px; | ||
color: #8f98a1; | ||
|
||
&.is-active span { |
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.
Should we also add a hover state for the font color change or do you think this is something we should override in the nav repo?
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.
I think all buttons will need their own color overrides to the default Button component in this repo. I think this can be a follow up.
Hiya all! @psealock Oh Wow! Thank you for getting the ball rolling on this :D. I know this is still early in development! Hopefully there's still room for feedback. I've been researching and experimenting very heavily in this space (Navigation components). I was planning on creating a draft PR with some ideas, but you beat me to it 😉 . I'm going to share some of my thoughts and findings (I hope that's okay!). Identifying RequirementsI've studied various (nested) navigation use cases from various websites, apps, mobile apps, design systems, and component libraries. All with the goal of understanding how we could apply some of those patterns to a potential Navigation component for WordPress/Gutenberg. I think I've boiled it down to handful of important requirements:
Other important implementation details include:
Router?The above behaviours are typically includes as part of a Router system. (e.g. React Router). Where we run into some issues is...
In other words... We'd like to have the feature-set of a Router, without directly using a library (like InspirationThe closest example of something that ticks all of the boxes is react-navigation (the go-to React Native solution). React Navigation allows the dev to create many different navigations (if needed). More importantly, these navs can be mixed together. Jumping between can be seamlessly triggered. AnatomyWe could break down the major parts of this Navigation menu into several parts:
(Note: Excuse the pseudo JSX code. I've naively simplified it for illustration purposes) An example of a configuration may look like this: const MyNav = () => {
return (
<Container>
<Screen id="Home" component={Home} />
<Screen id="Pages" component={Pages} />
<Screen id="Posts" component={Posts} />
</Container>
)
} Almost identical to ContainerThe TransitioningOn a state change, the Adapting to incoming changesThe For example, let's imagine the app setup looks something like this: <App>
<Sidebar>
<Container>
...
</Container>
</Sidebar>
<Main>
...
</Main>
</App> First, our Navigation container needs to know which <Container current={currentId}>
...
</Container> Let's say... we click on something in the If we are able to adjust the ScreenThe <Screen id="Pages" component={Pages} />
An example of an interesting (but still valid) use-case can be seen in some FSE navigation designs: If we continue our The Since the component isn't tied directly to Link / navigateThis is one of the trickier bits, as we may need to...
The simplest way I can think of to accomplishing this it to perhaps rely on a series of Here's an example: // screens/Settings.jsx
const Settings = () => {
const navigate = useNavigate()
const { checkSomething } = useDispatch(...)
return (
<>
<header>
<button onClick={navigate.goBack}>Back</button>
<h1>Settings</h1>
</header>
<ul>
<li>
<Link onClick={() => navigate.to('Settings/Advanced')}>
Advanced
</Link>
</li>
<li>
<Link onClick={checkSomething}>
Load More
</Link>
</li>
</ul>
</>
)
} The React Navigate's doc about navigating describes it quite well! AestheticsFeel like these navigation components should output unstyled HTML. The reason for this is because how Gutenberg/WP may need it to look, may different for 3rd party extensions like Woo (if Woo and others decide the use the same components). It would be difficult for 3rd party to adopt this and to have them fight with existing styles. We can see this hands-off approach taken by libraries like Reakit to great success! I feel like this navigation component may be able to achieve similar results, especially for the Example: <Link to="Settings" as={MyCustomLink} className="my-custom-className">Settings</Link> breathes Oh boy! That was a long one. Thank you all your time and consideration 🙇♂️. This is very tricky (but exciting) component! I'm happy that I'm able to participate. |
@ItsJonQ Thats great feedback, you've done lots of thinking on the subject! What you are describing is a fully controlled component that handles many aspects of navigation from routing, linking, and presenting content. The nature of the problem, as I see it, is one of placing a sidebar component in existing environments complete with their own routing solutions (or not), page controllers and/or presentational logic. I think the path forward is creating a "dumb" component whose inputs are a data tree, a current screen id, and click handlers (or urls). If we remove the requirement for an internal state or router or any navigational logic, we can concentrate on a component that is solely responsible for presenting the appropriate tree, animating between screens, updating solely based on outside changes in screen id, and url navigation or executing a callback handed down as a prop. The architectural ideas described can exist and be built around such a component. By removing the logic from this component in favor of a more presentational component, you can apply it to many more scenarios, including one with all the things described in the comment above.
I agree. I think I will remove the styling that isn't layout oriented. We can always revisit this later. |
Thanks for taking the time to lay out your feedback, @ItsJonQ!
Agreed on all points.
Does this mean transition renders for the nav component itself or for the content of the page?
This is where keeping the component “dumb” (presentational) really shines. The navigation component only needs to know which menu items you have and which is active. With each menu item we can pass a callback parameter so that any component consuming the navigation can decide what happens. This allows the callback to:
The advantage to this situation is that it will fit into any existing ecosystem. For the WooCommerce Navigation project this is particularly important since much of the navigation occurs on traditional WordPress screens that have not yet been Reactified. In terms of
I tend to agree with this. My only question is how “unstyled” do we want this component to be? Most Gutenberg components contain some level of styling, but I do agree keeping it minimal is important. |
93ac90c
to
645018d
Compare
UpdateThere is now a feature branch for developing this component incrementally https://github.com/WordPress/gutenberg/tree/feature/navigation. Here is a list of follow up issues which we can create and add the
|
Is there worry that the |
Transition renders for the nav component.
I understand the arguments for a purely presentational component set. Will continue thought with...
My idea was for a navigation system that would provide (intelligent) bare-bone primitives. Let's pretend this are called On top of that, we could provide pre-configured stylized UI pieces (that use Having the 2 pieces offers more flexibility for integrating into various scenarios. WooCommerce has a different use-case compared to Gutenberg, where we may need to first use it in FSE. Presentation onlyWe could start in this manner. Something that we may not be able to support (in the beginning) may be the animation transitions between arbitrary nested menus. If we do, I suggest that the component should be broken down into smaller pieces. Example:
etc...
As @jameskoster has pointed out, there are varying use-cases. The setup of the nav should be less prescriptive. Having the pieces allows the user (dev) to compose as needed. StylesI would also advise using CSS-in-JS An example of this would be something like Hope this helps! |
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 for getting the base up and tackling some of that feedback. I think we should get this one merged in so we can unblock some of these issues and continuing iterating.
Also noting that based on @ItsJonQ's feedback above, we'll also need issues for:
- Breaking down components into smaller pieces (we can move away from the array of data and
primary|secondary
potentially. - Converting styles to css-in-js.
I think both of the above items are reasonable, but let me know if you have any differing opinions on the structure here, @psealock.
<div className="components-navigation-items"> | ||
{ items.map( ( item ) => | ||
! item.isSecondary ? ( | ||
<Item |
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.
If we go forward with @ItsJonQ's suggestion then I think we will end up removing primary
and secondary
logic altogether and this can be determined by the navigation plugin consuming this component.
Thanks for the great feedback again, especially the suggestions on naming and CSS in js. I agree its a good time to merge this into the base branch, create issues and begin pull requests.
I'm having trouble at this time seeing how this would work, but I'm all in favor of being able to remove the secondary/primary logic. |
Good point @TimothyBJacobs. How about |
That sounds good to me! Could maybe also be |
I'm not entirely sure we can get rid of it, but if we move towards more atomic components, we can build out the navigation using these components like this: <Navigation>
<NavigationHeader>Menu Title</NavigationHeader/>
<NavigationMenu>
<NavigationItem>Item from primary</NavigationItem>
</NavigationMenu>
<NavigationMenu>
<NavigationItem>Item from secondary</NavigationItem>
</NavigationMenu>
</Navigation> Then the WC nav project is responsible for adding primary/secondary patterns in. Or if we want a little bit more opinion in these components, we might consider using a prop like I gave approval above, but I guess my permissions are not sufficient to merge in GB. We may need to ping someone on the GB team. |
Haiii! Just a heads up, I've started exploring the mechanics of this component in my experimental component library: It's not complete yet, but there may be some ideas there that can help with the development of this component within Gutenberg. Feel free to poke around at the code + demos! |
* Initial working tree display * add styles * add styles * dark theme * back button * secondary * BEM style classnames * remove back button styles * add menu: 'primary' data propery * get even more working * remove extra styles * add README * manifest
* Initial working tree display * add styles * add styles * dark theme * back button * secondary * BEM style classnames * remove back button styles * add menu: 'primary' data propery * get even more working * remove extra styles * add README * manifest
* Initial working tree display * add styles * add styles * dark theme * back button * secondary * BEM style classnames * remove back button styles * add menu: 'primary' data propery * get even more working * remove extra styles * add README * manifest
Add a page navigator component for use in the Block Editor and WooCommerce's Navigation project.
See a full demo here to see WooCommerce's application and check out a screenshot from Block Editor designs below:
Current state
This is a work in progress. To see the latest, use Storybook by running
npm run storybook:dev
.How has this been tested?
Screenshots
Types of changes
Checklist: