reactjstypescriptsvgchakra-uiforeignobject

Menu's dropdown on Chart is being offset


I'm adding a chakra-ui menu dropdown button to a chart (from react-financial-charts, which is a library built over svg).

For some reason, when I click on the menu, there will be whitespace between the button and the dropdown menu. This only happens when I put the menu onto the chart. If I have the menu standalone in the browser, it'll work as expected.

enter image description here

This is the menu code:

function TestMenu() {
  return (
    <g className="react-financial-charts-enable-interaction">
      <foreignObject
        x={30}
        y={30}
        width={"100%"}
        height={"100%"}
        style={{ overflow: "auto" }}
      >
        <Menu>
          <MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
            Actions
          </MenuButton>
          <MenuList>
            <MenuItem>Download</MenuItem>
            <MenuItem>Create a Copy</MenuItem>
            <MenuItem>Mark as Draft</MenuItem>
            <MenuItem>Delete</MenuItem>
            <MenuItem>Attend a Workshop</MenuItem>
          </MenuList>
        </Menu>
      </foreignObject>
    </g>
  );
}

This is the full codesandbox:

https://codesandbox.io/s/nervous-haze-3mz2c?file=/src/BasicLineSeries.tsx:511-1199

EDIT

If I remove x={0} and y={0} from foreignObject and include style={{ marginTop: "30px", marginLeft: "30px" }} into MenuButton instead, as suggested by one of the answers, this will solve the problem only if the chart is at the top of the page. Otherwise, if there is a div before the chart, then this will occur:

enter image description here

and here's the full codesandbox for that:

https://codesandbox.io/s/nostalgic-pare-c5rxu?file=/src/BasicLineSeries.tsx


Solution

  • UPDATED

    According to the menu list position problem, you can make use of Portal to move the entire list options to the bottom of the DOM so that its style will not be affected by any style/component inside the Chart.

    ...
    <Menu>
      <MenuButton
        as={Button}
        rightIcon={<ChevronDownIcon />}
        transition="all 0.001s"
        borderRadius="md"
        borderWidth="0px"
        _hover={{ bg: "gray.400" }}
        _expanded={{ bg: "blue.400" }}
        _focus={{ boxShadow: "none" }}
        style={{ marginTop: "30px", marginLeft: "30px" }} // better move the style to css file 
      >
        Actions
      </MenuButton>
      <Portal>
        <MenuList zIndex={10}>
          <MenuItem>Download</MenuItem>
          <MenuItem>Create a Copy</MenuItem>
          <MenuItem>Mark as Draft</MenuItem>
          <MenuItem>Delete</MenuItem>
          <MenuItem>Attend a Workshop</MenuItem>
        </MenuList>
      </Portal>
    </Menu>
    ...
    

    The Updated Codesandbox


    The white space is triggered by the foreignObject's x and y where the MenuList is respecting the space specified in foreignObject. (You can try to increase x and y, you will see a larger gap between the button and menu list)

    To solve the problem, you can remove the x and y and apply the margin on MenuButton

    ...
    <foreignObject
      width={"100%"}
      height={"100%"}
      style={{ overflow: "auto" }}
    >
      <Menu>
        <MenuButton
          as={Button}
          rightIcon={<ChevronDownIcon />}
          transition="all 0.001s"
          borderRadius="md"
          borderWidth="0px"
          _hover={{ bg: "gray.400" }}
          _expanded={{ bg: "blue.400" }}
          _focus={{ boxShadow: "none" }}
          style={{ marginTop: "30px", marginLeft: "30px" }} // better move the style to css file
        >
          Actions
        </MenuButton>
        <MenuList>
          <MenuItem>Download</MenuItem>
          <MenuItem>Create a Copy</MenuItem>
          <MenuItem>Mark as Draft</MenuItem>
          <MenuItem>Delete</MenuItem>
          <MenuItem>Attend a Workshop</MenuItem>
        </MenuList>
      </Menu>
    </foreignObject>
    ...
    

    Here is the modified codesandbox