Conversation
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
|
||
| ### Unavailable menu items | ||
|
|
||
| Wrap a `<MenuItem>` and a `<Popover>` with a `<UnavailableMenuItemTrigger>` and apply `isUnavailable` to disable the item's default action and show an informational popover instead. |
There was a problem hiding this comment.
I opted to have put the Popover directly into the example rather than create a UnavailableMenuItemTrigger wrapper in the starters since I felt it might be confusing to have Popover mentioned here but not show up in the example. Open to opinions though, not too bothered if we wanna go the same route as SubmenuTrigger and wrap the Popover in the starters
| // TODO: consider if we should instead pull the RAC defined trigger here into S2. Feels useful for RAC users though, but | ||
| // maybe shouldn't be specific to unavailable. It is very similar to SubmenuTrigger but it renders a dialog type behavior (see useSubmenuTrigger) | ||
| // and has the concept of isUnavailable which will make the item render normally when false | ||
| function UnavailableMenuItemTrigger(props: UnavailableMenuItemTriggerProps): JSX.Element { |
There was a problem hiding this comment.
UnavailableMenuItemTrigger seems a bit specific in RAC honestly, open to opinions if there should be a more generic naming/component
|
Build successful! 🎉 |
| // TODO: this is quite specific, but description and keyboardShortcut above are also very specific | ||
| /** Props for the descriptive element in the end slot inside the menu item (e.g. info icon, chevron). */ | ||
| endSlotProps: DOMAttributes, | ||
|
|
There was a problem hiding this comment.
I'm quite iffy about this, as stated above it is very specific. I think ideally we'd give the user a way to make arbitrary child elements in a MenuItem part of that MenuItem's aria-describedby. I could've opted to just allow the user to provide a generic aria-describedBy to the menu item and then apply ids to various other elements but we already have description and keyboard shortcut handled so figured we may be able to make end slot a thing too
There was a problem hiding this comment.
Also note that due to how collection items work in RAC (aka UnavailableMenuItemTrigger in RAC is a collection branch that only renders the popover child as is), I believe that RAC level change is unavoidable since there isn't a good way to generate a id at the S2 UnavailableMenuItemTrigger level and propagate it down via context
There was a problem hiding this comment.
What about allowing 'aria-describedby' as a prop and merging that with the ones we generate? Then this could be generated by S2 MenuItem and passed in. The only question would be in what order do we do the merge?
There was a problem hiding this comment.
I can go this route instead, just felt potentially confusing to the end user that they'd be expected to pass aria-describedby for some things but that we'd handle doing this for such specific things like keyboard shortcut icon and description.
I think we'd should merge the user's provided value first. Is the concern here the announcement order or more so what would happen if the user provided their own ids to the keyboard icon/description and then used those in their aria-described?
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Build successful! 🎉 |
|
Build successful! 🎉 |
## API Changes
react-aria-components/react-aria-components:MenuItemRenderProps MenuItemRenderProps {
allowsDragging?: boolean
hasSubmenu: boolean
isDisabled: boolean
isDragging?: boolean
isDropTarget?: boolean
isFocusVisible: boolean
isFocused: boolean
isHovered: boolean
isOpen: boolean
isPressed: boolean
isSelected: boolean
+ isUnavailable?: boolean
selectionBehavior: SelectionBehavior
selectionMode: SelectionMode
}/react-aria-components:UnavailableMenuItemTrigger+UnavailableMenuItemTrigger {
+ children: Array<ReactElement>
+ isUnavailable?: boolean = false
+}/react-aria-components:EndSlotContext+EndSlotContext {
+ UNTYPED
+}/react-aria-components:UnavailableMenuItemTriggerProps+UnavailableMenuItemTriggerProps {
+ children: Array<ReactElement>
+ isUnavailable?: boolean = false
+}@react-aria/menu/@react-aria/menu:MenuItemAria MenuItemAria {
descriptionProps: DOMAttributes
+ endSlotProps: DOMAttributes
isDisabled: boolean
isFocusVisible: boolean
isFocused: boolean
isPressed: boolean
keyboardShortcutProps: DOMAttributes
labelProps: DOMAttributes
menuItemProps: DOMAttributes
}@react-spectrum/s2/@react-spectrum/s2:UnavailableMenuItemTrigger+UnavailableMenuItemTrigger {
+ children: Array<ReactElement>
+ isUnavailable?: boolean = false
+}/@react-spectrum/s2:UnavailableMenuItemTriggerProps+UnavailableMenuItemTriggerProps {
+ children: Array<ReactElement>
+ isUnavailable?: boolean = false
+} |
| * | ||
| * @version alpha | ||
| */ | ||
| export const UnavailableMenuItemTrigger = /*#__PURE__*/ createBranchComponent(UnavailableMenuItemTriggerNode, (props: UnavailableMenuItemTriggerProps, ref: ForwardedRef<HTMLDivElement>, item) => { |
There was a problem hiding this comment.
What are the differences between this and SubmenuTrigger? All of our popovers are dialogs now (even submenus) so it seems like we could use SubmenuTrigger for this already? Unavailable seems like a very spectrum concept to me.
There was a problem hiding this comment.
The differentiating factor is the behavior differences that come into play when providing type: 'dialog' to the useSubmenuTrigger hook:
react-spectrum/packages/@react-aria/menu/src/useSubmenuTrigger.ts
Lines 145 to 149 in 50e009e
With submenus, hitting escape will close ALL levels of the menu tree, ArrowLeft will close the submenu, and we may autofocus the first item in the submenu.
I definitely agree with Unavailable being a spectrum concept though, as mentioned here https://github.com/adobe/react-spectrum/pull/9657/changes#r2849513111, happy to make this SubDialogTrigger instead perhaps
| const MenuItemContext = createContext<ContextValue<MenuItemContextProps, HTMLDivElement>>(null); | ||
|
|
||
| /** Context for the descriptive element in the end slot of the menu item (e.g. info icon). Propagates the appropriate labelling props to associate the element with the parent menu item. */ | ||
| export const EndSlotContext = createContext<ContextValue<{id?: string}, HTMLElement>>(null); |
There was a problem hiding this comment.
I don't see where this is consumed?
There was a problem hiding this comment.
consumed by S2 MenuItem:
react-spectrum/packages/@react-spectrum/s2/src/Menu.tsx
Lines 490 to 495 in b58662f
|
|
||
| /** | ||
| * Transforms ContextualHelpTrigger: | ||
| * - Rename ContextualHelpTrigger to UnavailableMenuItemTrigger. |
There was a problem hiding this comment.
I assume you discussed renaming this somewhere? Is it not specific to ContextualHelp anymore? I think the original idea was that it was also not specific to unavailable and people might use it to provide other kinds of help info but not sure we ever supported that?
There was a problem hiding this comment.
Nope, didn't really discuss this anywhere, but a personal feeling that ContextualHelp (arbitrarily attached to any element) and ContextualHelpTrigger (menu) were too similarly named thus was confusing.
As for making it not specific to unavailable, I wasn't aware that was the direction we wanted to go as a team, but I had the same thought here. Happy to change this to be SubDialogTrigger or something along those line if so
There was a problem hiding this comment.
some additional thoughts: if it wasn't specific to unavailable (and were more just informational), would that imply that the wrapped menu item should still be actionable? If so, I guess we'll need to add another prop so the user can decide whether or not the menu level onAction makes it to the item at the RAC level. At the S2 level, it would also need to differentiate between the isUnavailable=true case, the isUnavailable=false case, and the "is rendering a informational subdialog case"
| // TODO: this is quite specific, but description and keyboardShortcut above are also very specific | ||
| /** Props for the descriptive element in the end slot inside the menu item (e.g. info icon, chevron). */ | ||
| endSlotProps: DOMAttributes, | ||
|
|
There was a problem hiding this comment.
What about allowing 'aria-describedby' as a prop and merging that with the ones we generate? Then this could be generated by S2 MenuItem and passed in. The only question would be in what order do we do the merge?
Closes
✅ Pull Request Checklist:
📝 Test Instructions:
Test both the S2 and RAC implementations in both storybook and docs.
🧢 Your Project:
RSP