-
Notifications
You must be signed in to change notification settings - Fork 1
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
New Accessible Dropdown Nav Bar Component #478
base: main
Are you sure you want to change the base?
Conversation
- remove baseUrl and nav link util - use Link when local path - create more unique key/id without needing to pass in one - update tests
.dropdown-nav-bar.no-mobile { | ||
@import 'dropdown-nav-bar-no-mobile'; | ||
@import 'dropdown-nav-bar-common'; | ||
} | ||
|
||
.dropdown-nav-bar:not(.no-mobile) { | ||
@import 'dropdown-nav-bar-common'; | ||
} |
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 allowed me to use the same styles for both implementations and just change the $tablet
sass variable for the no-mobile
version.
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.
Interesting! I'll defer to @marchwiany on best practices in using dynamic variables as this is pretty foreign to me.
For my own edification, would the following not perform a similarly sequenced import? Maybe in the compilation step the second block is executed before the first?
.dropdown-nav-bar.no-mobile {
@import 'dropdown-nav-bar-no-mobile';
}
.dropdown-nav-bar {
@import 'dropdown-nav-bar-common';
}
<input | ||
type="checkbox" | ||
id="mobile-nav-button" | ||
checked={isMobileMenuOpen} | ||
onChange={() => { | ||
setIsMobileMenuOpen(!isMobileMenuOpen) | ||
}} | ||
/> | ||
<label htmlFor="mobile-nav-button" className="mobile-menu"> | ||
<span /> | ||
<span /> | ||
<span /> | ||
</label> |
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.
Pulled in the mobile menu button used on AZ, but we might have a more up to date version to swap in.
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 walk me through the decision to not include this as part of the 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.
@marchwiany Do we typically use buttons or are we still mostly using the checkbox approach?
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.
Looking great so far! I have a handful of questions, so it might be helpful to schedule some time to go through them sync
@@ -0,0 +1,91 @@ | |||
& { |
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 haven't seen this usage before! What does &
signify when it's used at the top-level?
@@ -64,6 +64,7 @@ | |||
@import "components/search-results-and-filtering"; | |||
@import "components/recruiter-search"; | |||
@import "components/tabs"; | |||
@import "components/dropdown-nav-bar/dropdown-nav-bar"; |
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 need to be ported over to the client template as well or will the styles automatically be applied when a user imports the component into their project?
.dropdown-nav-bar.no-mobile { | ||
@import 'dropdown-nav-bar-no-mobile'; | ||
@import 'dropdown-nav-bar-common'; | ||
} | ||
|
||
.dropdown-nav-bar:not(.no-mobile) { | ||
@import 'dropdown-nav-bar-common'; | ||
} |
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.
Interesting! I'll defer to @marchwiany on best practices in using dynamic variables as this is pretty foreign to me.
For my own edification, would the following not perform a similarly sequenced import? Maybe in the compilation step the second block is executed before the first?
.dropdown-nav-bar.no-mobile {
@import 'dropdown-nav-bar-no-mobile';
}
.dropdown-nav-bar {
@import 'dropdown-nav-bar-common';
}
#mobile-nav-button { | ||
position: absolute; | ||
top: -9999px; | ||
left: -9999px; | ||
|
||
&:checked { | ||
& ~ .dropdown-nav-bar .dropdown-nav-menu { | ||
top: 0; | ||
position: fixed; | ||
overflow: auto; | ||
display: block; | ||
height: 100%; | ||
width: 100%; | ||
} | ||
|
||
& ~ .mobile-menu { | ||
span:nth-child(1) { | ||
transform: translateY(8px) rotate(45deg); | ||
} | ||
span:nth-child(2) { | ||
opacity: 0; | ||
} | ||
span:nth-child(3) { | ||
transform: translateY(-8px) rotate(-45deg); | ||
} | ||
} | ||
} | ||
} | ||
|
||
.mobile-menu { | ||
position: absolute; | ||
right: 15px; | ||
z-index: 3; | ||
cursor: pointer; | ||
|
||
@include media($tablet) { | ||
display: none; | ||
} | ||
|
||
span { | ||
display: block; | ||
margin: 4px auto; | ||
height: 4px; | ||
width: 25px; | ||
background: #333; | ||
transition: 0.5s; | ||
} | ||
} |
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's the distinction between these styles and the ones included in _dropdown-nav-bar-common
?
} | ||
const menuItemOnBlur = () => { | ||
if (hideSubmenuButtonBeforeFocus) { | ||
// timeout needed to move from link to button without it disappearing |
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.
Could you expand on this a bit? What's moving from the link to the button? What is "it"?
{isExternalPath ? ( | ||
<a href={path} {...menuItemProps}> | ||
{name} | ||
</a> | ||
) : ( | ||
<Link to={path} {...menuItemProps}> | ||
{name} | ||
</Link> | ||
)} |
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 might be a useful abstraction here. Something like IsomorphicLink
(inspired by https://github.com/streamich/react-use/blob/master/src/useIsomorphicLayoutEffect.ts)?
function IsomorphicLink ({ path, ...rest }) {
const isExternalPath = path.startsWith('http') // or whatever the logic ends up being
return isExternalPath ? <a href={path} {...rest} /> : <Link to={path} {...rest} />
}
* | ||
* @name DropdownNavBar | ||
* @type Function | ||
* @description A control component for navigating through multiple navigation menu items that can include dropdowns with submenu items along with a mobile view option |
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 document the expected keyboard functionality here? If you have any links to ARIA authoring practices or other resources that inspired you, that'd be helpful as well!
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 there any need for arrow key support? It looks like the authoring practices specifically call out the situation where we have a menuItem that also performs a function:
As well as some other keyboard requirements:
Reference: https://www.w3.org/TR/wai-aria-practices-1.1/#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.
There might be some room for interpretation here, but from the following quotes and this example, it seems like using tab
to navigate between the items may not be appropriate?
When a menu opens, or when a menubar receives focus, keyboard focus is placed on the first item. All items are focusable as described in § 6.6 Keyboard Navigation Inside Components.
6.6 Keyboard Navigation Inside Components
As described in section § 6.1 Fundamental Keyboard Navigation Conventions, the tab sequence should include only one focusable element of a composite UI 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.
Ah, that number 4 note is the pattern I had trouble finding! I had moved away from using the arrow navigation vs tab navigation as I wasn't sure that was a common pattern when the parent menu item was also a link, but this sounds pretty clear to me. I'm working on that refactor now.
})} | ||
role="none" | ||
> | ||
<OutsideClickHandler onOutsideClick={closeDesktopSubmenus}> |
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.
Similar question as before - are there use cases for closing all submenus vs. just the relevant submenu? Or should all submenus always be closed here?
If only one submenu can be open at a time, does that suggest that the value captured should be submenuId
and not submenuIds
?
const menuItemProps = { | ||
onFocus: menuItemOnFocus, | ||
onBlur: menuItemOnBlur, | ||
role: 'menuItem', |
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 this be 'menuitem'
? Reference: https://www.digitala11y.com/menuitem-role/
Provides a contained and simple to stand up accessible navigation bar with a menu of parent links and optional child links hidden within submenus.
Known Issues
Author Checklist