Platform: Mega Menu component Technical Design - SAP/fundamental-ngx GitHub Wiki

Obsolete tech spec- replaced by Menu/Menu Trigger

Mega Menu Component

Summary

The mega menu is a menu with a multi-level hierarchy. It has a top-level menu and submenus. The expand icon beside a menu item triggers the submenu.

Like the Menu Component, Mega Menu can also contain icons, separators, and grouped items. A Mega Menu can be nested up to 2 levels.

The items in the parent menu can be grouped(still part of parent-menu), have children(the sub-menu), be selected, have custom labels and can have callbacks for specific click actions.

Design:

<fdp-mega-menu 
        [menuItems] = 'megaMenuData' 
        [showSeparator]='true|false'
        [isScrolling]='true|false' 
        [scrollLimit]='any number' 
        [width]="'width-in-px'" >
</fdp-mega-menu>

Property Bindings

megaMenuData : MegaMenuItem[] or MenuGroup[]

is an array of items of type MegaMenuItem or MenuGroup.

MegaMenuItem
MegaMenuItem consists of: 
* `label`- the name of the parent item or group header. (Required)
* `icon`- icon to display for all items part of this group. (Optional)
* `secondaryIcon`- another icon to display at the other end of the item. (Optional)
* `selectable`- menu item selectable. If item is selectable, then it will display selected/non-selected state when toggled. (Optional)
* `selected`- if this item is selected. (Optional)
* `disabled`- if this item is to appear in disabled state. (Optional)
* `tooltipLabel`- a customized label to show up on tooltip that is different from `label`. (Optional)
* `childItems`- all the **MegaMenuItem**s that will form the sub-menu. (Optional)
* `callback`- the function to be called when this item is clicked. Could navigate to another page or do any other action. (Required)
* `expanded`- whether the sub-menu should be opened or not. Default is set to false. (Optional)

Note:

-command should not be allowed in the following cases:

  1. if item has childItems, since clicking on parent item only opens sub-menu and does nothing else.

  2. if item has groupItems, since clicking on group header does nothing else.

Do note that the childItems and groupItems can themselves have command(), only their parent item cannot.

-MegaMenuItem with childItems cannot be selectable or disabled.

MenuGroup
MenuGroup consists of: 
* `label`- the name of the item. (Required)
* `icon`- icon to display in front of the menu label. (Optional)
* `tooltipLabel`- a customized label to show up on tooltip that is different from `label`. (Optional)
* `groupItems`- all the **MegaMenuItem**s that are part of this group. (Optional)
* `command()`- the function to be called when this item is clicked. Could navigate to another page or do any other action. (Required)

An example of how megaMenuData would look like is:

megaMenuData: (MegaMenuItem | MegaMenuSubItems)[] = [];

//we have two `MenuGroup`s here, one with childItems(represents a sub-menu) 
//and one with groupItems(represents a menu group).

this.megaMenuData = [{
        label: 'First Parent Item',
        icon: 'sap-icon--vehicle-repair',
        childItems: [{                        //Represents sub-menu items
              label: 'First Parent Sub Item 1',
              selectable: true,
              selected: true,
              command: () => {
                alert("First Parent Sub Item 1 called");
              }
            },
            {
              label: 'First Parent Sub Item 2',
              disabled: true,
              tooltipLabel: 'Second level item',
              command: () => {
                alert("First Parent Sub Item 2 called");
              }
            }]
      },
      {
        label: 'Parent Group 1',
        groupItems: [                    //Represents group header and its items in parent menu
           {
              label: 'Item 1 in Group 1',
              disabled: true,
              command: () => {
                alert("Item 1 in Group 1 called");
              }
           },
           {
              label: 'Item 2 in Group 1',
              tooltipLabel: 'Another item',
              command: () => {
                alert('Item 2 in Group 1');
              }
           }
        ]
      },
      {
        label: 'Third Parent Item',
        icon: 'sap-icon--grid',
        command: () => {
          alert('Third parent item called');
        }
      }]

showSeparator: boolean

showSeparator specifies if a separating line should be added between menu items. This field is optional. Default is set to false.

isScrolling: boolean

isScrolling specifies if the menu should scroll. This will apply scrolling to Mega Menu globally; to parent and its sub-menus. This field is optional. Default is set to false.

scrollLimit: number

scrollLimit specifies the number of items to be displayed after which scrolling begins. This will apply scrolling to Mega Menu globally; to parent and its sub-menus. This field is optional. Default is set to the total number of items in order to display all items.

width: string

width specifies the maximum width the Menu and sub-menu should take. Minimum width is set to 192px. This field is optional.


Event Bindings

N/A


Two-Way Bindings

N/A


Content Projection

N/A


Interfaces

MegaMenuItem

extends from: MenuItem. Showcasing below all the fields that will be used(including ones from MenuItem)

export interface MegaMenuItem extends MenuItem {
    label: string;

    icon?: string;

    secondaryIcon?: string;

    selectable?: boolean;

    selected?: boolean;

    tooltipLabel?: string;

    disabled?: boolean;

    childItems: MegaMenuItem[];

    expanded?: boolean;

    command()?: void;
}

MenuGroup

can reuse existing one MenuGroup

export interface MenuGroup {
    label?: string;

    icon?: string;

    tooltipLabel?: string;

    groupItems: MegaMenuItem[];
}

Related Modules

N/A

Additional Notes


Questions

For Manju-

  1. Does the child menu also need to be grouped? It can become more complex than it already is.

--- The child menu need not be grouped, since it already defines 'menu items of a particular type'. Groups can remain in parent level alone.

  1. FRD specifies max of 2-level nesting. What do we mean by 2-level nesting; 2 sub-sub-levels or 1 parent and 1 sub-level?

--- 2-level means parent->sub-menu->sub-sub-menu.

  1. Since it is 2-level nesting, should sub-items themselves have children->sub-sub-items?

--- yes, same as above, but not more than that.

  1. Are we defining scrolling for sub-menus as well? If yes, we need to think how user will specify scrolling for sub-menu and if he can give separate values to parent menu and sub-menu. i.e. scrolling false for parent, and true for sub-menu.

--- Two ways to go about this:

1. scrolling should be present for child as well as parent, but how will we customise scrolling and scrollLimit for parent and child separately? - low priority

2. same values of scrollLimit apply across all levels. For now, we should implement this.

  1. What if user does not specify any child elements? Are we forcing user to specify children to qualify as mega menu? Should children be mandatory for at least one item?

--- children need not be mandatory. It can be optional, but it should look like a normal menu component. We are not enforcing any restriction to enforce Mega Menu, it is just a guideline.

  1. Parent menu that has children(sub-menu) cannot be selectable or disabled, or can it be? Is there a requirement that if parent is marked as selected or disabled, its child sub-menu items are all marked disabled/selected?

--- Parent item can be marked as disabled. However, it does not affect the children(sub-menu), since mouse pointer anyway does not work. However, children can individually be marked disabled and need not reflect the same to parent(in case for 3 child-items, all 3 are marked disabled, the parent item need not be marked disabled).

Parent cannot be marked as selectable, but its children can be.


Comments

@Frank

  • There already some similar component and we should sync up. Like tabs. All have could have MenuItems and MegaMenu is really similar

---We should probably discuss more on this in the call

  • How do you pass items into it? You probably forget a binding - lets call it [list]="menuItems"

---Currently I was passing it as local template reference through #menuItems. I will modify it to [menuItems]="menuItems"

  • Let;s just call the MegaMenuItems => MenuItem without the s

---Do you mean MegaMenuItems should be renamed to MenuItem or is this the same as Kevin's comment where he asks to get rid of MegaMenuSubItems? Does MenuItem represent the items in the sub-menu?

Update: Have modified the Wiki with using MegaMenuItem(which extends MenuItem) and MenuGroup alone. No more MegaMenuSubItem

  • You dont need to have subitems type you can use the same time Menu Item
    • see following properties for the Menu Item
      • label,
      • icon,
      • id, - ---why is this needed? Also, what will this be? An auto incrementing number?
      • routerList?, I can click and navigate to some route - ---how is this different from callback/command, where user can write code for navigation.
      • itemns - the same type as root type - ---does this mean that there is no limit to number of sub-menus user can have? Currently, FRD specifies limit of 2 levels of sub-menus.
      • definitelly your callback, but I would call it command or action, plus some others you have
      • expanded - when this one has subm menu items you want to also keep some state about expansion . - ---open is already present in ngx mega-menu-item directive. Also, are we going to reuse ngx's mega menu directives? Seems like mega menu item component might help us.

---So we should have two interfaces internally : MegaMenuItems which could be the parent menu, and MenuItem which is what any menu item should have including the above mentioned properties like label, icon, id etc. ?

  • Binding separator should be more descriptive - showXXX, hasXXXX

    • Since the default behavior is False, it should be called showSeparator to turn it on

    --- okay, have changed the name to showSeparator

  • Binding separator should be more descriptive - showXXX, hasXXXX

  • Do you have some exmaples (pictures) how it looks when it scrolls? and some usecases?

---Have asked Manju to help out with this. The Menu FRD consists an image of Menu with scrolling.

  • I would not limit this only to pixes.

---do you mean the width attribute? I am using this width to calculate item width when secondary icon is used so that truncation works correctly and the secondary icon is aligned correctly. The unit must be agreed upon, it need not be px, it could be em or anything else too.

  • If the customLabel is just a tooltip/hint, let;s just call this hint or description

---okay, will change to tooltipLabel.

  • We shoudl try to reuse some elementary component and Menu/MegaMenu share the same

---How about we try to reuse MenuItem/MenuGroup that is already present in shellbar menu? We could also reuse MegaMenuItem component that is already available in NGX?

  • Once you start with menu not MegaMenu as MegaMenu should use menu you will then be able to also template rendered item
   <fdp-mega-menu 
        [list]="items"
        [showSeparator]='true'>
           <ng-template #item let-item>
               <menu-item ...
           </ng-template>
</fdp-mega-menu>

  • core also support custom separator for menu - this can be done with the same ng-template mechanism

@Kevin

  • I agree with Frank that we probably don't need the sub-menu type MegaMenuSubItems but we may want to incorporate the MenuGroup interface which was introduced in the Menu component.

---How do we incorporate MenuGroup into MegaMenu? Can we have another interface that extends MenuGroup so that we can customize adding MegaMenu specific requirements like sub-items into it?

  • For the MenuItem interface, can we rename customLabel to tooltipLabel as it is more descriptive of what it actually is?

Update: Have modified the Wiki with using MegaMenuItem(which extends MenuItem) and MenuGroup alone. No more MegaMenuSubItem

---Will modify it to tooltipLabel.

Updates after Oct 3rd discussion:

Changes to Wiki needed:

  • No need for a separate MegaMenuItem that extends MenuItem. Keep childItems as optional field in MenuItem itself; if needed the same MenuItem can hold a sub-menu as well.

  • id field is needed in case MenuItem is used as a MenuBar or for Tab component where we need to know which tab index to move to. It could be anything unique. Having id also helps with testing.

  • routerLink allows to navigate to other pages in the app. However, since command can also do this, we do not need a separate routerLink field.

  • See if we can reuse MegaMenuItemComponent available in core.

  • Handling different values of isScrolling and scrollLimit for sub-menus will only add to the complexity, therefore let it be be one value that affects all sub-menus.

  • width field is needed in our case because we are using overflow:ellipsis alongwith display:inline-flex which conflict with each other and the only workaround is to explicitly provide a width field. We should be able to handle px, em, rem though.

Open-ended:

  • If we pass data(especially label) in the current way, how do we handle i18n?

  • For scrolling, should we be using the Scrollbar component? The Scrollbar component consists of a compact look and default look, so managing this will be difficult. What are the usecases of this and is it really needed?