Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocomplete with Popper breaks positioning #13179

Closed
2 tasks done
Duskfall opened this issue Oct 10, 2018 · 22 comments · Fixed by #17037
Closed
2 tasks done

Autocomplete with Popper breaks positioning #13179

Duskfall opened this issue Oct 10, 2018 · 22 comments · Fixed by #17037
Assignees
Labels
component: autocomplete This is the name of the generic UI component, not the React module! new feature New feature or request

Comments

@Duskfall
Copy link

Duskfall commented Oct 10, 2018

I would like for the position of the list to change if I filter based on specific value.
Right now if I filter the position stays the same and seems wrong, and if I scroll with my mouse then it correctly places itself above the input

  • This is not a v0.x issue.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior

Position of the Popper should be recalculated on input change

Current Behavior

Position doesn't get recalculated, only on scroll.

Steps to Reproduce

  1. Open https://material-ui.com/demos/autocomplete/
  2. Make the window smaller so that you can scroll down.
  3. Place the input of the autocomplete right at the bottom
  4. Enter a letter
  5. Options should get rendered above the input
  6. Enter another letter
  7. You will encounter the problem as in screenshot

Context

Use autocomplete

Your Environment

Just the https://material-ui.com/demos website.
Browser: Chrome Version 69.0.3497.100 (Official Build) (64-bit)

image

@bogdansoare
Copy link

also having this issue

@khrizt
Copy link

khrizt commented Dec 4, 2018

That also happens when the number of items in autocomplete (using downshift + popper approach and top positioning) reduces from the max entries.

@eps1lon
Copy link
Member

eps1lon commented Dec 4, 2018

Basic issue is that the offset is only calculated on open and window resize. Using a resize observer would be possible but needs a 2Kb polyfill for IE 11 (we could just use it if it's available).

Another solution is to measure the element in gSBU and cDU and then change the style again if the size changed. Basically a react based resize observer.

@khrizt
Copy link

khrizt commented Dec 4, 2018

@eps1lon I get the measure part, but how can we force to change the style when the size has changed?

@eps1lon
Copy link
Member

eps1lon commented Dec 4, 2018

That was more aimed at a possible fix to our codebase.

A workaround for now is to make use of the action prop in the Popover and call updatePosition on that when you think it's right. https://material-ui.com/api/popover/#props

@khrizt
Copy link

khrizt commented Dec 5, 2018

Ok, thanks anyway. I couldn't make autocomplete (with downshift) work with Popover, so I probably will hold material-ui migration until this is resolved.

@Filson14
Copy link

@eps1lon, Your idea is OK, but there is a problem with downshift and popover that when <Popover /> is open, it blocks other elements so you are no longer enabled to type in.
Great solution would be something similar like popover actions - to be able to reposition <Popper /> (even manually) like on scroll or resize.
I tried popperOptions - no success.

@eps1lon
Copy link
Member

eps1lon commented Feb 15, 2019

@Filson14 This issue is not about the popover blocking input. Could you open an issue if that is the case for you?

It looks like Popper is currently missing an API to update the position similar to Popover? I think that would be ok to add @oliviertassinari ?

As a temporary workaround you can force a remount of the Popper when you would call Popover#updatePosition by changing the key: https://codesandbox.io/s/0xzrvxrmrl

@eps1lon eps1lon added the new feature New feature or request label Feb 15, 2019
@oliviertassinari
Copy link
Member

@eps1lon I agree, this sounds like an excellent idea!

@Filson14 What do you think of:

--- a/docs/src/pages/demos/autocomplete/IntegrationDownshift.js
+++ b/docs/src/pages/demos/autocomplete/IntegrationDownshift.js
@@ -244,6 +244,15 @@ const styles = theme => ({
 });

 let popperNode;
+let updatePosition;
+
+function UpdatePosition() {
+  if (updatePosition) {
+    updatePosition();
+  }
+
+  return null;
+}

 function IntegrationDownshift(props) {
   const { classes } = props;
@@ -310,7 +319,13 @@ function IntegrationDownshift(props) {
                 popperNode = node;
               },
             })}
-            <Popper open={isOpen} anchorEl={popperNode}>
+            <Popper
+              action={actions => {
+                updatePosition = actions.updatePosition;
+              }}
+              open={isOpen}
+              anchorEl={popperNode}
+            >
               <div {...(isOpen ? getMenuProps({}, { suppressRefError: true }) : {})}>
                 <Paper
                   square
@@ -328,6 +343,7 @@ function IntegrationDownshift(props) {
                 </Paper>
               </div>
             </Popper>
+            <UpdatePosition />
           </div>
         )}
       </Downshift>
diff --git a/packages/material-ui/src/Popper/Popper.js b/packages/material-ui/src/Popper/Popper.js
index fb53a9c42..f8f32359e 100644
--- a/packages/material-ui/src/Popper/Popper.js
+++ b/packages/material-ui/src/Popper/Popper.js
@@ -40,6 +40,18 @@ class Popper extends React.Component {
     };
   }

+  componentDidMount() {
+    if (this.props.action) {
+      this.props.action({
+        updatePosition: () => {
+          if (this.popper) {
+            this.popper.scheduleUpdate();
+          }
+        },
+      });
+    }
+  }
+
   componentDidUpdate(prevProps) {
     if (prevProps.open !== this.props.open && !this.props.open && !this.props.transition) {
       // Otherwise handleExited will call this.
@@ -139,6 +151,7 @@ class Popper extends React.Component {

   render() {
     const {
+      action,
       anchorEl,
       children,
       container,
@@ -186,6 +199,15 @@ class Popper extends React.Component {
 }

 Popper.propTypes = {
+  /**
+   * This is callback property. It's called by the component on mount.
+   * This is useful when you want to trigger an action programmatically.
+   * It currently only supports updatePosition() action.
+   *
+   * @param {object} actions This object contains all possible actions
+   * that can be triggered programmatically.
+   */
+  action: PropTypes.func,
   /**
    * This is the DOM element, or a function that returns the DOM element,
    * that may be used to set the position of the popover.

feb-15-2019 23-43-59

It's a quick POC, we would be happy to review a pull request :).

@oliviertassinari oliviertassinari added the good first issue Great for first contributions. Enable to learn the contribution process. label Feb 15, 2019
@Filson14
Copy link

Nice idea with the key! Thanks.
By "blocking input" I mean that after opening a Popover, input's focus is lost and we are not able to continuously write down a search phrase. I thought it could be connected with backdrop but after disabling it, there is no difference. I don't think about it as an issue, beacause of different use case scenario between Popover and Popper (I guess).

Before typing
image

After typing - cursor is gone, focus lost
image

@eps1lon
Copy link
Member

eps1lon commented Feb 18, 2019

@Filson14 It is intended that a opening a popover moves focus to it. It's a modal that has autofocus and focus trap built into it. I don't think that's the right tool to use here.

Popover spreads additional props to the Modal. You might be able to fix that specific issue with some combination of disableEnforceFocus and disableAutoFocus. Again I caution against this since it might cause other issues with a11y.

I would recommend you stick with the demo since it should already cover most a11y concerns.

@Filson14
Copy link

Like I said, I get it 😉 In my case, using Popper with key work OK. Thanks again.

@joshwooding joshwooding added the hacktoberfest https://hacktoberfest.digitalocean.com/ label Sep 30, 2019
@oliviertassinari oliviertassinari removed good first issue Great for first contributions. Enable to learn the contribution process. hacktoberfest https://hacktoberfest.digitalocean.com/ labels Oct 6, 2019
@oliviertassinari oliviertassinari self-assigned this Oct 6, 2019
@oliviertassinari
Copy link
Member

An update on the solution I have proposed in #13179 (comment).

  • The popper update logic problem was solved by exposing a ref a few months ago. You can find an example of its usage in the CustomizedSlider.tsx file.
  • The integration examples will soon be legacy with [Autocomplete] Introduce new component #17037.

@oliviertassinari oliviertassinari added component: Autocomplete and removed component: Popper The React component. See <Popup> for the latest version. labels Oct 6, 2019
@roonyh
Copy link

roonyh commented Dec 19, 2019

@oliviertassinari is the fix shipped? I am using Version 4.0.0-alpha.36. But still seeing this issue.

@oliviertassinari
Copy link
Member

This issue covers a legacy version of the component. I'm sorry, it's off topic.

@roonyh
Copy link

roonyh commented Dec 19, 2019

I am using material-ui/lab version 4.0.0-alpha.36. And I am having this exact issue. Should I submit a new issue?

@oliviertassinari
Copy link
Member

Yes please, we need a reproduction too.

@mikkovor
Copy link

mikkovor commented Dec 31, 2019

I was having this exact same problem and after 2 hours of bashing my head against the wall since the demo on mui site doesn't have this problem I decided to upgrade both core and lab to latest versions and it started working.

My versions when it wasn't working:
"@material-ui/core": "^4.5.1"
"@material-ui/lab": "^4.0.0-alpha.35"

If anyone else is having this issue just upgrade to latest.

@oliviertassinari oliviertassinari added the component: autocomplete This is the name of the generic UI component, not the React module! label Apr 29, 2020
@hadasmaimon
Copy link

@Filson14 I use the autocomplete component,
and I want to change the drop-down that shows options to look like you showed (to be smaller and moves according to the mouse focus) how do I do it with mui?

Current look:
image

Expected look:
image

@oliviertassinari any help, please?

@anmolmalik97
Copy link

anmolmalik97 commented Dec 14, 2020

if autocomplete is inside a container that contains a zoom property. popper position is always incorrect.

<div style={{zoom: 0.85}}> <Autocomplete/> </div>

Any workaround to avoid this?

@oliviertassinari
Copy link
Member

@anmolmalik97 You can replace the PopperComponent with a simple div.

@anmolmalik97
Copy link

@oliviertassinari thank you for such a quick response. I tried using it. But using div as a popperComponent pushes the other components away when it opens up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: autocomplete This is the name of the generic UI component, not the React module! new feature New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.