From a4fc1c5b44a8260efa890726e32d6d756db26260 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Mon, 21 Aug 2023 17:26:41 -0400 Subject: [PATCH] Submenus (#1527) * [RFC] Proof-of-concept commit for submenu support This is a Request For Comments to seek directional guidance towards implementing the submenu slot of menu-item. Includes: - SubmenuController to manage event listeners on menu-item. - Example usage in menu-item documentation. - Trivial tests to check rendering. Outstanding questions include: - Accessibility concerns. E.g. where to handle 'ArrowRight', 'ArrowLeft'? - Should selection of menu-item denoting submenu be possible or customizable? - How to parameterize contained popup? - Implementation concerns: - Use of ref / id - delegation of some rendering to the controller - What to test Related to [#620](https://github.com/shoelace-style/shoelace/issues/620). * Update submenu-controller.ts Removed extraneous `console.log()`. * PoC working of ArrowRight to focus on submenu. * Revert "PoC working of ArrowRight to focus on submenu." (Didn't mean to publish this.) This reverts commit be04e9a221e7b9e38995297e70d4517b3fae6468. * [WIP] Submenu WIP continues. - Submenus now close on change-of-focus, not a timeout. - Keyboard navigation support added. - Skidding fix for better alignment. - Submenu documentation moved to Menu page. - Tests for accessibility, right and left arrow keys. * Cleanup: Removed dead code and dead code comments. * style: Eslint warnings and errors fixed. npm run verify now passes. * fix: 2 changes to menu / submenu on-click behavior: 1. Close submenu on click explicitly, so this occurs even if the menu is not inside of an sl-dropdown. 2. In menu, ignore clicks that do not explicitly target a menu-item. Clicks that were on (e.g. a menu-border) were emitting select events. * fix: Prevent menu's extraneous Enter / space key propagation. Menu's handleKeyDown calls item.click (to emit the selection). Propagating the keyboard event on Enter / space would the cause re-entry into a submenu, so prevent the needless propagation. * Submenu tweaks ... - 100 ms delay when opening submenus on mouseover - Shadows added - Distance added to popup to have submenus overlap menu slightly. * polish up submenu stuff * stay highlighted when submenu is open * update changelog * resolve feedback --------- Co-authored-by: Bryce Moore --- docs/pages/components/dropdown.md | 91 +++++- docs/pages/components/menu.md | 109 ++++++++ docs/pages/components/select.md | 6 +- docs/pages/resources/changelog.md | 3 + .../menu-item/menu-item.component.ts | 61 +++- src/components/menu-item/menu-item.styles.ts | 18 +- src/components/menu-item/menu-item.test.ts | 111 ++++++++ .../menu-item/submenu-controller.ts | 262 ++++++++++++++++++ src/components/menu/menu.component.ts | 13 +- 9 files changed, 658 insertions(+), 16 deletions(-) create mode 100644 src/components/menu-item/submenu-controller.ts diff --git a/docs/pages/components/dropdown.md b/docs/pages/components/dropdown.md index da08f881..b1876056 100644 --- a/docs/pages/components/dropdown.md +++ b/docs/pages/components/dropdown.md @@ -310,6 +310,96 @@ const App = () => ( ); ``` +### Submenus + +To create a submenu, nest an `` element in a [menu item](/components/menu-item). + +```html:preview + + Edit + + + Undo + Redo + + Cut + Copy + Paste + + + Find + + Find… + Find Next + Find Previous + + + + Transformations + + Make uppercase + Make lowercase + Capitalize + + + + +``` + +```jsx:react +import SlButton from '@shoelace-style/shoelace/dist/react/button'; +import SlDivider from '@shoelace-style/shoelace/dist/react/divider'; +import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown'; +import SlMenu from '@shoelace-style/shoelace/dist/react/menu'; +import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item'; + +const css = ` + .dropdown-hoist { + border: solid 2px var(--sl-panel-border-color); + padding: var(--sl-spacing-medium); + overflow: hidden; + } +`; + +const App = () => ( + <> + + Edit + + + Undo + Redo + + Cut + Copy + Paste + + + Find + + Find… + Find Next + Find Previous + + + + Transformations + + Make uppercase + Make lowercase + Capitalize + + + + + +); +``` + +:::warning +As a UX best practice, avoid using more than one level of submenu when possible. +::: + ### Hoisting Dropdown panels will be clipped if they're inside a container that has `overflow: auto|hidden`. The `hoist` attribute forces the panel to use a fixed positioning strategy, allowing it to break out of the container. In this case, the panel will be positioned relative to its [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#Identifying_the_containing_block), which is usually the viewport unless an ancestor uses a `transform`, `perspective`, or `filter`. [Refer to this page](https://developer.mozilla.org/en-US/docs/Web/CSS/position#fixed) for more details. @@ -349,7 +439,6 @@ Dropdown panels will be clipped if they're inside a container that has `overflow import SlButton from '@shoelace-style/shoelace/dist/react/button'; import SlDivider from '@shoelace-style/shoelace/dist/react/divider'; import SlDropdown from '@shoelace-style/shoelace/dist/react/dropdown'; -import SlIcon from '@shoelace-style/shoelace/dist/react/icon'; import SlMenu from '@shoelace-style/shoelace/dist/react/menu'; import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item'; diff --git a/docs/pages/components/menu.md b/docs/pages/components/menu.md index 2039960a..dd9c662d 100644 --- a/docs/pages/components/menu.md +++ b/docs/pages/components/menu.md @@ -44,3 +44,112 @@ const App = () => ( :::tip Menus are intended for system menus (dropdown menus, select menus, context menus, etc.). They should not be mistaken for navigation menus which serve a different purpose and have a different semantic meaning. If you're building navigation, use `