microsoft / fast

The adaptive interface system for modern web experiences.
https://www.fast.design
Other
9.28k stars 597 forks source link

rfc: add `MenuButton` #6236

Closed KingOfTac closed 5 months ago

KingOfTac commented 2 years ago

MenuButton

Overview

As defined by the W3C:

A menu button is a button that opens a menu. It is often styled as a typical push button with a downward pointing arrow or triangle to hint that activating the button will display a menu.

Background

A common request in the FAST community is how to make dropdowns, either for action menus or navigation. It is also a very common pattern amongst UI libraries, and while it is something that can produced using a combination of components that already exist in FAST it still a difficult pattern to implement correctly when accessibility becomes involved.

Use Cases

Features

Risks and Challenges

Prior Art/Examples


Design

API

Component Name:

Attributes & Properties:

Events

Anatomy

Notes:

<!-- shadow dom -->
<button
  id="{buttonId}"
  class="control"
  part="control"
  aria-haspopup="true"
  aria-controls="{menuId}"
  aria-expanded="{expanded || null}"
>
  <slot name="start"></slot>
  <slot></slot>
  <slot name="end"></slot>
  <span class="indicator" part="indicator">
    <slot name="expanded-icon">
      {MenuButtonOptions.expandedIcon}
    </slot>
    <slot name="collapsed-icon">
      {MenuButtonOptions.collapsedIcon}
    </slot>
  </span>
</button>
<div
  id="{menuId}"
  class="menu"
  part="menu"
  role="menu"
  aria-labelledby="{buttonId}"
>
  <slot name="item">
    Menu items / dividers and other presentation content go here.
    Anything that is not `role=menuitem` is ignored by keyboard navigation
  </slot>
</div>

Slot Names

CSS Parts


Implementation

States

Accessibility

The MenuButton should align to the patterns provided by the W3C: https://www.w3.org/WAI/ARIA/apg/patterns/menubutton/

Globalization

The MenuButton will need to reverse the start and end slots, as well as the indicator for users in an RTL setting. The menu positions for start and end will also need to reverse as well.

Dependencies


Appendix

Resources

W3C Spec for Menu, Menubar, and MenuButton W3C Accessible MenuButton Example

KingOfTac commented 2 years ago

I don't how I missed #5469 when looking for existing issues tracking this.

KingOfTac commented 2 years ago

WIP Examples

chrisdholt commented 2 years ago

Thanks for this! One immediate thought that I have here is that I think the current RFC details two implementations. What I mean by that is that I think this illustrates both the foundation implementation, as well as what I would expect from the CLI. Specifically, I wonder about the "universal" applicability of menu-width, menu-height, and menu-offset. I really like these for the "FAST CLI" starter implementation, where those can be driven by tokens, but I don't think there are necessary for every menu button (foundational). Thoughts on that distinction @KingofTac? Again, I really like those, but they aren't necessary for an implementation where I may have a menu button with a menu that is holistically controlled by CSS as one example.

KingOfTac commented 2 years ago

From a foundation point of view, I agree they aren't needed.

Getting into the implementation specifics, I would say if floating-ui is used by the foundation class, it would be pretty easy for implementations to provide their own version of computePosition and add the specific middewares and positioning details that they want. So with that in mind the CLI starter could very well add the menu-width and menu-height as tokens and override the foundation MenuButton's computePosition and pass it the size middleware using those tokens.

chrisdholt commented 2 years ago

From a foundation point of view, I agree they aren't needed.

Getting into the implementation specifics, I would say if floating-ui is used by the foundation class, it would be pretty easy for implementations to provide their own version of computePosition and add the specific middewares and positioning details that they want. So with that in mind the CLI starter could very well add the menu-width and menu-height as tokens and override the foundation MenuButton's computePosition and pass it the size middleware using those tokens.

This was exactly the consideration I had when working to incorporate Floating-UI into a couple of our components as part of #6186. My thought was that while it might make for a robust API, exposing everything would make for a very "cluttered" API by default. Instead, I figured a better way might be to have a public method that could be overridden to provide a more custom and bespoke implementation of computePosition (something like setMenuPosition()).

A couple of follow-up thoughts/questions here:

  1. Ideally I'd like to be able to use menu, because that's a not-insignificant portion of code that does represent a menu. As noted, it doesn't represent all types of menu, but we can look to pick that work up either as part of menu or by creating a more common navigation "menu" approach (list w/ links) for example...

  2. If we have labelledby in the shadow dom for the menu, but the role of menu is on a slotted element, does that do what we expect accessibility wise? I'm not sure it's going to pick it up as expected.

  3. Do you have an example of the implementation/prototype that was causing issues w/ NVDA (is that in the WIP examples)?

KingOfTac commented 2 years ago
  1. I think using Menu is perfectly doable. The challenges with it are, like you said it doesn't represent all menus.

  2. This comment made me realize I missed adding the menu role in the anatomy section, so I went and updated that above. I think you're right that labelledby won't cross the shadow boundary so that's why I intended the menu element to be a part of MenuButton's anatomy. I can see cases where you would want to slot in a custom menu element, so thoughts/ feedback here would be great.

  3. I'll have to find where I have that prototype since that implementation was from several months ago. I'll link here when I find it.

KingOfTac commented 2 years ago

@chrisdholt Not a fully functioning menu button, but this shows the Behavior I was seeing with NVDA and using Button https://stackblitz.com/edit/typescript-lh92fh?file=index.ts

When the menu button receives focus, it will announce that it is clickable more than once

KingOfTac commented 2 years ago

@chrisdholt I'm just circling back to this. If you want, I can update the spec to be focused on the foundation implementation with notes on extending and implementing custom versions.

chrisdholt commented 2 years ago

@chrisdholt I'm just circling back to this. If you want, I can update the spec to be focused on the foundation implementation with notes on extending and implementing custom versions.

I think that would be great. Having the spec focus on Foundation and then illustrate extensibility as examples will transition well for the CLI and documentation IMO. I'd love if our documentation broadened to open up examples of extending classes to address common scenarios

KingOfTac commented 2 years ago

Sounds good. I'll get a new version up this weekend.

JudahGabriel commented 2 years ago

Hi, I'm interested in building a split button and dropdown button components for FAST. What's the status of this project? Obviously you've got the spec here, but has any work been done to implement this?

I see two other issues asking for similar stuff:

Also, there was a (discarded?) PR to add a split button in the past.

So stepping back a moment, two questions:

  1. For @KingOfTac what's the status of this menu button? Has code been written yet?
  2. For @chrisdholt, I'm hesitant to contribute here for fear my work would be discarded. Would FAST welcome a MenuButton, SplitButton, and NavBar? Or is this something you guys don't want, or would rather build yourselves?
KingOfTac commented 2 years ago

Hi, I'm interested in building a split button and dropdown button components for FAST. What's the status of this project? Obviously you've got the spec here, but has any work been done to implement this?

I see two other issues asking for similar stuff:

Also, there was a (discarded?) PR to add a split button in the past.

So stepping back a moment, two questions:

  1. For @KingOfTac what's the status of this menu button? Has code been written yet?

  2. For @chrisdholt, I'm hesitant to contribute here for fear my work would be discarded. Would FAST welcome a MenuButton, SplitButton, and NavBar? Or is this something you guys don't want, or would rather build yourselves?

This spec is still rather new. It needs more work and there are still a few unknowns around whether or not to compose existing components into this one or not. I have been working on a new version of the spec that I hope we'll be able to start implementing.

Another reason why there hasn't been much new component work is that most of the team has been focused on the next major release of FAST which is turning out to be one of the largest with many new features and breaking changes. For this reason I have been hesitant to start implementation until vNext is closer to release in order to reduce refactoring due to breaking changes in fast-element and fast-foundation.

JudahGabriel commented 2 years ago

Ah, interesting indeed. Well, I'm beginning to question whether I should wait to contribute then. Thanks for the heads up.

@chrisdholt want to weigh in? Would it be wise to wait to contribute until FAST vNext is released?

chrisdholt commented 2 years ago

Hi, I'm interested in building a split button and dropdown button components for FAST. What's the status of this project? Obviously you've got the spec here, but has any work been done to implement this?

I see two other issues asking for similar stuff:

It appears there was a (discarded?) PR to add a split button in the past.

This all makes me a bit hesitant to contribute here

Well, first - we'd welcome a contribution here. Specific to the split button work, the hope was to bring that into a more well rounded menu button approach, perhaps via a slot (noted here: https://github.com/microsoft/fast/pull/4083#issuecomment-1030250715) because it lends itself better to our goals. The biggest concern with serving both menu button (as represented in this issue) and split button are that our primary method of approach is composition. A year or two ago there was a good amount of discussion around split buttons and their patterns - one big thing that landed was that they are two tab stops. They are both buttons, one being a normal button and one following the menu button pattern (still a button). So when exploring split button, I tend to ask how those things "compose", considering this is two buttons and we value composition, the most intuitive template for a composable split button is either one or two slots and the class itself likely doesn't need any logic because the element just organizes slotted content. Given that context, it was proposed in the spec review that perhaps split button as a construct is better served by being slotted in with a menu button. This would allow slotting the second via the light DOM for full control, etc. We decided we wanted to explore that route rather than deliver something that was a generic wrapper for buttons - Brook was going to pick that back up, but he ended up moving on to a different project and the need for that component fell off (until now). There is more to that backstory, but hopefully that gives at least some insight into why that was closed and nothing materialized from it.

I think the key here is that we'd love a contribution to FAST for any number of things component-wise and we certainly don't need to be the ones to build it ourselves. With that said though, I think any contribution directly to fast-foundation for instance needs to go through some kind of spec review to ensure that we're aligned in our approach, goals are being met, etc. We'd happily take a navbar, or navigation menu (we don't have that...basically a list w/ anchors), or other things - but I think the expectation for it bubbling into foundation is that it's aligned with our current approach and values - hopefully that makes sense. An example would be - if your concept for a menu is to forgo slots and composition in favor of taking in item data from an array; that's certainly doable but it doesn't align with our current approach of "composition first". We'll gladly take contributions (and we do quite often), but in order for us to provide consistent experience we need to ensure that the approach is consistent with our goals. With that said, if you're up for it, we'd welcome a contribution and we're happy to help work through certain nuances. Additionally, perhaps you have an idea for how we can approach composition while providing a great DX out of the box - we'd love to hear that and see how our approach might evolve.

chrisdholt commented 2 years ago

Ah, interesting indeed. Well, I'm beginning to question whether I should wait to contribute then. Thanks for the heads up.

@chrisdholt want to weigh in? Would it be wise to wait to contribute until FAST vNext is released?

Eh....I say no :). It depends though, I know that could be a bit chaotic.

We're exploring a few things that I think are really exciting right now, but due to gaps I haven't put together an RFC on the direction. Even then, I don't think it would impact the work you'd be contributing in super significant ways...perhaps some light refactoring but I don't see it significantly changing approaches for instance.

@EisenbergEffect may have thoughts here too but in terms of creating new components, I think most of the breaks that would be most painful have already been made on the FAST Element side of things.

EisenbergEffect commented 2 years ago

In terms of component building, I think you can use the v2 stuff in master with the v1 documentation. There aren't really breaking changes in how components are built and we've made the primary organizational changes in foundation. So, I think you're good to go.

JudahGabriel commented 2 years ago

We'd happily take a navbar, or navigation menu (we don't have that...basically a list w/ anchors), or other things - but I think the expectation for it bubbling into foundation is that it's aligned with our current approach and values - hopefully that makes sense. An example would be - if your concept for a menu is to forgo slots and composition in favor of taking in item data from an array; that's certainly doable but it doesn't align with our current approach of "composition first".

Alright, that does make sense.

I'd be doing this contribution as part of Microsoft's Fix / Hack / Learn (FHL) week, meaning I have limited time to do this. Probably not enough time to build a well-rounded spec and component that conforms to W3C specs, but maybe enough to get started.

If anything, I could start a spec and create a proof-of-concept component(s) during FHL week, then get feedback from you all and iterate on that in my spare time.

chrisdholt commented 2 years ago

We'd happily take a navbar, or navigation menu (we don't have that...basically a list w/ anchors), or other things - but I think the expectation for it bubbling into foundation is that it's aligned with our current approach and values - hopefully that makes sense. An example would be - if your concept for a menu is to forgo slots and composition in favor of taking in item data from an array; that's certainly doable but it doesn't align with our current approach of "composition first".

Alright, that does make sense.

I'd be doing this contribution as part of Microsoft's Fix / Hack / Learn (FHL) week, meaning I have limited time to do this. Probably not enough time to build a well-rounded spec and component that conforms to W3C specs, but maybe enough to get started.

If anything, I could start a spec and create a proof-of-concept component(s) during FHL week, then get feedback from you all and iterate on that in my spare time.

Sounds good - and it doesn't have to be exhaustive. I'd provide as much content as needed and no more - we are pretty open to be iterative, etc. The big thing we're looking for is to identify any gaps in assumption or understanding and provide feedback up front to avoid someone going in an opposite direction or one that misses a few gotchas, requirements, etc.

JudahGabriel commented 2 years ago

Also, for some context here, the reason I'm interested in contributing these components is because I work on apps.microsoft.com, and we're looking to rewrite our front-end using web components. It's currently in React + old FAST (before Web Components!), but we're considering migrating to newer tech, and one option is modern FAST.

Looking at modern FAST and doing some quick tech spikes, I noticed it was missing some components we need, such as nav bar with collapsible menus on mobile, split buttons, and menu buttons. Hence why I'm interested in contributing. 🙂

chrisdholt commented 2 years ago

Contributions welcome - rising tide raises all ships and whatnot :)

KingOfTac commented 2 years ago

@chrisdholt @EisenbergEffect @JudahGabriel

New MenuButton spec that explores supporting split buttons. Per comments above, this merely positions the buttons and does not incorporate logic specific to split buttons.

Some basic exploratory examples here. https://stackblitz.com/edit/typescript-zg9qwx?file=index.html

MenuButton V2

Overview

As defined by the W3C:

A menu button is a button that opens a menu. It is often styled as a typical push button with a downward pointing arrow or triangle to hint that activating the button will display a menu.

Background

A common request in the FAST community is how to make dropdowns, either for action menus or navigation. It is also a very common pattern amongst UI libraries, and while it is something that can produced using a combination of components that already exist in FAST it still a difficult pattern to implement correctly when accessibility becomes involved.

Use Cases

Features

Risks and Challenges

Prior Art/Examples


design

API

Component Name:

Attributes & Properties:

Events

Anatomy

Notes: When the host element gets connected to the DOM, it will need to setup a click handler on the document in order to trigger the light dismiss of the menu.

MenuButtonOptions

<div part="control">
  <slot name="start"></slot>
  <fast-button
    aria-haspopup="true"
    aria-expanded="{expanded || null}"
  >
    <slot></slot>
    {
      options
        ? expanded
          ? options.expandedIcon
          : options.collapsedIcon
        : ''
    }
  </fast-button>
  <slot name="end"></slot>
</div>
<fast-menu
  role="menu"
  part="menu"
>
  <slot name="item">
    Menu items / dividers and other presentation content go here.
    Anything that is not `role=menuitem` is ignored by keyboard navigation
  </slot>
</fast-menu>

Slot Names

Notes: I'm on the fence on whether the default slot should be the internal button's content or the menu items. On the one hand you don't need to wrap the button's text with an element to slot it, but then you need to specify slot="item" on every item which becomes tedious and adds clutter to the markup.

CSS Parts


Implementation

States

Accessibility

Globalization

The MenuButton will need to reverse the start and end slots, as well as the indicator for users in an RTL setting. The menu positions for start and end will also need to reverse.

Dependencies

Appendix

Resources

W3C Spec for Menu, Menubar, and MenuButton W3C Accessible MenuButton Example

janechu commented 5 months ago

Unfortunately @microsoft/fast-foundation is being deprecated, refer to #6955.