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

Create the menu and menuitem components in kodiak ui #36

Conversation

alexanderchan
Copy link
Collaborator

Summary

To implement the category navigation in the templates Dialog, we will need to create two new components in Kodiak UI. The Menu component will be used to display and style vertical and horizontal menus. Eventually these components would replace the components that we use to build the Jilt application nav.

Right now, we will focus on providing only a vertical menu that is used in an aside like what is in the design and will add the ability to create a horizontal menu by passing in a variant to the component.

<Menu>
  <MenuItem onClick={() => // do something}>Item 1</MenuItem>
  <MenuItem onClick={() => // do something}>Item 1</MenuItem>
</Menu>

Main Story: ch30340

###Menu props

Name Value Default Required
variant string null false
variantKey string null false
aria-label string null false

MenuItem props

MenuItem props

MenuItem will accept all styled-system props. The isCurrent prop will add the appropriate styling for the menu item and utilize the primary theme color as the accent color.

Name Value Default Required
disabled boolean false false
isCurrent boolean false true
onClick () => void null true
variant string null false
variantKey string null false
as React.ElementType 'a' false

We will default the as prop to the anchor tag to ensure that the types always return with an href attribute if we want to render a MenuItem with an anchor tag.

Accessibility - https://www.w3.org/WAI/tutorials/menus/structure/

The Menu component should render a nav element at the root and the list should be rendered in an unordered list (

    ).

    All menu components should render with a label associated with them. A string can be passed to the aria-label prop to render the following HTML.

    <Menu aria-label="Main navigation">
      ...
    </Menu>
    
    // renders
    <nav aria-label="Main navigation">
      <ul>
        ...
      </ul>
    </nav>
    

    The currently selected menu item should be updated for screen readers to understand which item is the currently selected item. The MenuItem component will need to support the following approaches.

    Invisible text

    When the MenuItem is not rendering an anchor tag, we will need to add some invisible text for the screen reader to read. This will be used when rendering a menu item for categories or a non-page change interaction.

    <MenuItem as="span" isCurrent>
      <VisuallyHidden>Current Category: </VisuallyHidden>
      Product announcements
    </MenuItem>
    
    // renders
    <li class="current">
      <span>
        <span class="visuallyhidden">Current Page: </span>
        Product announcements
      </span>
    </li>
    

    Using WAI-ARIA

    This technique should be used when we need to render an anchor tag in the MenuItem. The aria-current="page" will indicate the current page to screen readers.

    <MenuItem href="/contacts" isCurrent>
      Contacts
    </MenuItem>
    
    // renders
    <li class="current">
      <a href="/contacts" aria-current="page">
        Contacts
      </a>
    </li>
    

    🚦 Acceptance criteria

    • Will render a vertical navigation menu with the ability to compose menu items in it
    • Will contain a storybook story in Kodiak
    • Will be fully tested
    • Will allow for passing anchor links or static buttons to the menu

    Implementation Tasks

    Deployment

    Before merge to master

    Note: Code merged to master should be safe to automatically deploy to production as-is.

    • [QA] User-testing/quality assurance done

    After merge to master

    • Release: git push production master

@alexanderchan alexanderchan self-assigned this Feb 24, 2020
{...props}
>
<li>
{renderAs === 'a' && (
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@wesleycole I'm tempted to just take children and render them but then it's up to the calling component to set up some link defaults as well as the aria-current. It then becomes more important to look at the storybook/docs to see the sample of how it should be rendered.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yep, as we discussed in Slack, let's just accept children for now and its up to the developer to render the inside.

Copy link
Collaborator

@wesleycole wesleycole left a comment

Choose a reason for hiding this comment

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

This is looking really good just a few very small things.

Comment on lines 17 to 18
"prepare": "microbundle --jsx React.createElement -f es,cjs",
"watch": "microbundle watch --jsx React.createElement -f es,cjs"
Copy link
Collaborator

Choose a reason for hiding this comment

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

@alexanderchan I think we can get rid of the --jsx flag here as well. I think that was a copy and paste that I did. Mind taking care of that?

Comment on lines +18 to +19
"prepare": "microbundle -f es,cjs",
"watch": "microbundle --watch -f es"
Copy link
Collaborator

Choose a reason for hiding this comment

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

🏆

export type BaseProps = {
__base?: SxStyleProp
}

export function base(props: { theme: Theme; __base?: SxStyleProp }) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
export function base(props: { theme: Theme; __base?: SxStyleProp }) {
export function base(props: { theme: Theme } & BaseProps) {

SystemProps

export const Menu = React.forwardRef<HTMLDivElement, MenuProps>(
function Message({ children, variantKey = 'menu', ...props }, forwardedRef) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
function Message({ children, variantKey = 'menu', ...props }, forwardedRef) {
function Menu({ children, variantKey = 'menu', ...props }, forwardedRef) {

Copy link
Collaborator

Choose a reason for hiding this comment

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

@alexanderchan can we type the ref as a ul element?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unfortunately Box complains, we'll have to see if we can make use material-ui's approach to typing based on the as.

Copy link
Collaborator Author

@alexanderchan alexanderchan Feb 26, 2020

Choose a reason for hiding this comment

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

Oh found a way for now, I forced the type through at the Box level to be any and that fixes it so that the ref is the right type until we can do the as fix.

      <Box
        variantKey={variantKey}
        variant={variant}
        {...props}
        as={renderAs}
        ref={forwardedRef as any}
      >

Copy link
Collaborator

Choose a reason for hiding this comment

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

That works for me. Probably better for now anyway to be able to define which refs are coming in for the specific components when Box is so general.

import { Box } from '../Box'

type MenuProps = {
children?: React.ReactNode
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think children should be required, right?

SystemProps

export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
function Message(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
function Message(
function MenuItem(


type MenuItemProps = {
children: React.ReactNode
isCurrent?: boolean
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think isCurrent can be required.

href?: string
rel?: HTMLAnchorElement['rel']
as?: React.ElementType
} & React.HTMLAttributes<HTMLDivElement> &
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
} & React.HTMLAttributes<HTMLDivElement> &
} & React.HTMLAttributes<HTMLLIElement> &

VariantProps &
SystemProps

export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
export const MenuItem = React.forwardRef<HTMLLIElement, MenuItemProps>(

{...props}
>
<li>
{renderAs === 'a' && (
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yep, as we discussed in Slack, let's just accept children for now and its up to the developer to render the inside.

@alexanderchan alexanderchan marked this pull request as ready for review February 26, 2020 18:11
Copy link
Collaborator

@wesleycole wesleycole left a comment

Choose a reason for hiding this comment

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

Just a few questions, overall this is looking great!

} & React.ComponentProps<typeof Box>

export const Menu = React.forwardRef<HTMLUListElement, MenuProps>(function Menu(
{ children, variantKey = 'menu', variant, ...props },
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
{ children, variantKey = 'menu', variant, ...props },
{ children, variantKey = 'menus', variant, ...props },

I'm not necessarily saying that this change needs to be made but I think we need to define whether variantKeys should be singular or plural.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point, yes that makes sense, plural for the variantKey 👍

Comment on lines +12 to +13
variantKey = 'menuitem',
variant,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
variantKey = 'menuitem',
variant,
variantKey = 'menu',
variant = 'menuItem',

@alexanderchan what do you think about nesting this in the menu variantKey` similar to how we do with Dialogs?

@alexanderchan alexanderchan merged commit 4fc72b4 into master Feb 26, 2020
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