elastic / eui

Elastic UI Framework 🙌
https://eui.elastic.co/
Other
6.09k stars 835 forks source link

[EuiContextMenu] `renderItem` wildcard content #7507

Closed mdefazio closed 8 months ago

mdefazio commented 8 months ago

Describe the solution you'd like We would like to have more flexibility on the structure of the context menu when passing in an array of panel objects.

Background

Currently it is possible to pass an array of panel objects that generate the panel structure (alongside being able to build out the context menu by individual components). We would like to be able to have a bit more flexibility with the display of these panels when passing in an array.

cc/ @umbopepato @XavierM @andreadelrio

Expectation

We would like the option to include an option for 'inline' (or whatever makes sense) to a panel which would display this alongside the previous panel, and not a nested panel.

Note: this is just an initial thought, we are absolutely open to better implementation ideas here

An example of this could be the following:

Current setup:

const currentPanels=[
{
 id: 0,
 title: "Fruit",
 items: [
  {
    name: "Bananas",
    onClick: ()=>{...},
  },
  {
    name: "Oranges",
    onClick: ()=>{...},
  },
  {
    name: "Papayas",
    onClick: ()=>{...},
  },
  {
    name: "More fruit",
    icon: “boxesVertical”
    panel: 1,
  },
 ].
},
{
 id: 1,
 title: "More fruit",
 items: [
  {
    name: "Apples",
    onClick: ()=>{...},
  },
  {
    name: "Grapes",
    onClick: ()=>{...},
  },
  {
    name: "Pineapples",
    onClick: ()=>{...},
  },
 ].
}
]

image

Proposal:

const newPanels=[
{
 id: 0,
 title: "Fruit",
 items: [
  {
    name: "Bananas",
    onClick: ()=>{...},
  },
  {
    name: "Oranges",
    onClick: ()=>{...},
  },
  {
    name: "Papayas",
    onClick: ()=>{...},
  },
  {
    name: "More fruit",
    panel: 1,
  },
  {
    name: "Exotic fruit",
    icon: “starFilled”
    panel: 2,
  },
 ].
},
{
 id: 1,
 inline: true, // New option
 items: [
  {
    name: "Apples",
    onClick: ()=>{...},
  },
  {
    name: "Grapes",
    onClick: ()=>{...},
  },
  {
    name: "Pineapples",
    onClick: ()=>{...},
  },
 ].
},
{
 id: 2,
 inline: false, // New option
 items: [
  {
    name: "Durian",
    onClick: ()=>{...},
  },
  {
    name: "Carambola",
    onClick: ()=>{...},
  },
  {
    name: "Jackfruit",
    onClick: ()=>{...},
  },
 ].
}
]

image

cee-chen commented 8 months ago

From a DOM/animation perspective, this doesn't really make sense for us to do and would convolute the underlying code fairly severely. Could we instead give you individual items to render, e.g.

const panels = [
  {
    id: 0,
    title: 'Fruit',
    items: [
      // normal EuiContextMenuItem props
      { isSeparator: true },
      { isSubTitle: true, title: 'More fruit' },
      // more EuiContextMenuItems
  },
  {
    id: 1,
    title: 'More',
    items: [...],
  },
];

This matches the API we have in place for EuiCollapsibleNavBeta FWIW.

EDIT: actually, the most flexible API (which still matches what we do for EuiCollapsibleNavBeta) would be to give you a render prop for an item obj, e.g.

items: [
  { render: EuiHorizontalRule },
  { render: () => <EuiTitle size="xxxs"><h3>Subtitle</h3></EuiTitle> },
  { render: CustomComponent, }
]
umbopepato commented 8 months ago

Hey @cee-chen, thanks for your input! I think any of these solutions would work perfectly for our use case 🙂

cee-chen commented 8 months ago

renderItem prop it is! :) Thanks for your flexibility all!

edit: I misremembered the prop name!

mdefazio commented 8 months ago

Thank you!!

cee-chen commented 8 months ago

Hey y'all! This should be in Kibana main sometime this week.

Example usage:

const CustomComponent = () => (
  <div data-test-subj="custom">Hello world</div>
);

<EuiContextMenu
  initialPanelId={1}
  panels={[
    {
      id: 1,
      title: 'Testing renderItem',
      items: [
        // Default context menu item
        {
          name: 'Renders an EuiContextMenuItem',
          panel: 2,
        },
        // Custom items
        {
          renderItem: () => <EuiTitle size="xxs"><h3>Subtitle</h3></EuiTitle>,
        },
        {
          key: 'custom',
          renderItem: CustomComponent,
        },
      ],
    },
  ]}
/>

You can also see a custom subtitle here, on the second panel:

https://eui.elastic.co/pr_7510/storybook/index.html?path=/story/euicontextmenu--playground